2010-04-26 13 views
39

हमारे पास पर add(obj) पर कॉल करने वाले कई थ्रेड हैं।समवर्ती धागे एक ही समय में ऐरेलिस्ट में जोड़ते हैं - क्या होता है?

मेरा सिद्धांत यह है कि जब add को दो धागे द्वारा समवर्ती रूप से कहा जाता है, तो केवल दो वस्तुओं में से एक को जोड़ा जा रहा है वास्तव में ArrayList में जोड़ा गया है। क्या यह व्यवहार्य है?

यदि हां, तो आप इसके आसपास कैसे जाते हैं? एक सिंक्रनाइज़ संग्रह का उपयोग करें जैसे Vector?

उत्तर

42

क्या होता है इसके लिए कोई गारंटीकृत व्यवहार नहीं होता है जब ऐरे को ऐरेलिस्ट पर दो धागे द्वारा समवर्ती रूप से कहा जाता है। हालांकि, यह मेरा अनुभव रहा है कि दोनों वस्तुओं को ठीक जोड़ा गया है। सूचियों से संबंधित अधिकांश धागे सुरक्षा मुद्दे जोड़ते/हटाते समय पुनरावृत्ति के साथ सौदा करते हैं। इसके बावजूद, मैं कई धागे और समवर्ती पहुंच के साथ वेनिला ऐरेलिस्ट का उपयोग करने के खिलाफ दृढ़ता से अनुशंसा करता हूं।

वेक्टर समवर्ती सूचियों के लिए मानक होता था, लेकिन अब मानक Collections synchronized list का उपयोग करना है।

यदि आप जावा में थ्रेड के साथ काम करने के लिए किसी भी समय खर्च करने जा रहे हैं तो भी मैं गोएट्ज़ एट अल द्वारा प्रैक्टिस में जावा कंसुरेंसी की अत्यधिक अनुशंसा करता हूं। पुस्तक इस मुद्दे को बहुत बेहतर विस्तार से कवर करती है।

+13

"यह मेरा अनुभव रहा है कि दोनों वस्तुओं को ठीक जोड़ा गया है" बस यह इंगित करना चाहते हैं कि यह बहुत ही भाग्यशाली है। संभावित रूप से ArrayList के साथ डेटा भ्रष्टाचार के मुद्दे के लिए खिड़की बहुत छोटी है, लेकिन यह अभी भी –

+5

सही है, और यह मेरी तरह का मुद्दा है। अधिकांश मामलों में कोई समस्या नहीं होगी; लेकिन हम ज्यादातर मामलों में कार्यक्रम नहीं करते हैं। इस प्रकार मैंने ArrayList का उपयोग करने के खिलाफ दृढ़ता से क्यों अनुशंसा की। – derivation

2

यदि आप सरणीसूची के थ्रेड सुरक्षित संस्करण चाहते हैं तो आप List l = Collections.synchronizedList(new ArrayList()); का उपयोग कर सकते हैं।

0

http://java.sun.com/j2se/1.4.2/docs/api/java/util/ArrayList.html

ध्यान दें कि यह कार्यान्वयन सिंक्रनाइज़ नहीं कर रहा है। यदि एकाधिक धागे एक साथ ऐरेलिस्ट इंस्टेंस का उपयोग करते हैं, और कम से कम एक धागे संरचनात्मक रूप से सूची को संशोधित करता है, तो इसे बाहरी रूप से सिंक्रनाइज़ किया जाना चाहिए।

चूंकि आंतरिक रूप से कोई सिंक्रनाइज़ेशन नहीं है, जो आप सिद्धांतित करते हैं वह व्यवहार्य नहीं है।

तो, चीजें अप्रिय और अप्रत्याशित परिणामों के साथ सिंक से बाहर हो जाती हैं।

+0

ठीक है, उसने देखा। मैंने सोचा था कि एक 'ConcurrentModificationException' फेंक दिया गया होगा जो हमारे लिए नहीं हो रहा है .. हो सकता है कि हम एक थ्रेड सुरक्षित संग्रह का उपयोग न करने के प्रभाव को प्रभावित कर रहे हों .. –

+0

@Marcus जो केवल तभी होता है जब आप इस पर पुनरावृत्ति करते समय सूची को संशोधित करें। इसके अलावा, इस अपवाद को फेंकने की कोई गारंटी नहीं है। – WhirlWind

1

java.util.concurrent में एक थ्रेड-सुरक्षित सरणी सूची है। मानक ArrayList थ्रेड-सुरक्षित नहीं है और व्यवहार जब एक ही समय में एकाधिक थ्रेड अपडेट होते हैं तो अपरिभाषित होता है। एक से अधिक धागे एक ही समय में लिखते समय कई पाठकों के साथ अजीब व्यवहार भी हो सकते हैं।

3

आप null, ArrayOutOfBoundsException, या कार्यान्वयन के लिए कुछ छोड़ सकते हैं। HashMap एस उत्पादन प्रणालियों में एक अनंत लूप में जाने के लिए मनाया गया है। आपको वास्तव में यह जानने की ज़रूरत नहीं है कि क्या गलत हो सकता है, बस इसे मत करो।

आप Vector का उपयोग कर सकते हैं, लेकिन यह इंटरफ़ेस काम करने के लिए पर्याप्त समृद्ध नहीं है। आपको शायद पता चलेगा कि आप ज्यादातर मामलों में एक अलग डेटा संरचना चाहते हैं।

3

व्यवहार शायद अपरिभाषित है क्योंकि ArrayList थ्रेडसेफ नहीं है। यदि आप सूची को संशोधित करते समय सूची संशोधित करते हैं तो आपको एक समवर्ती मोडिफिकेशन अपवाद प्राप्त होगा। आप संग्रह.synchronizedList के साथ ArrayList को लपेट सकते हैं या थ्रेड-सुरक्षित संग्रह (कई हैं) का उपयोग कर सकते हैं, या केवल सिंक्रनाइज़ किए गए ब्लॉक में ऐड कॉल डाल सकते हैं।

14

कोई भी चीज हो सकती है। आप दोनों वस्तुओं को सही ढंग से जोड़ा जा सकता है। आप जोड़े गए वस्तुओं में से केवल एक ही प्राप्त कर सकते हैं। आप ArrayIndexOutOfBounds अपवाद प्राप्त कर सकते हैं क्योंकि अंतर्निहित सरणी का आकार ठीक से समायोजित नहीं किया गया था। या अन्य चीजें हो सकती हैं। यह कहने के लिए पर्याप्त है कि आप किसी भी व्यवहार पर भरोसा नहीं कर सकते हैं।

विकल्प, आप Vector इस्तेमाल कर सकते हैं के रूप में, आप Collections.synchronizedList इस्तेमाल कर सकते हैं, तो आप CopyOnWriteArrayList इस्तेमाल कर सकते हैं, या आप एक अलग ताला इस्तेमाल कर सकते हैं। यह सब इस बात पर निर्भर करता है कि आप और क्या कर रहे हैं और संग्रह के लिए आपके पास किस प्रकार का नियंत्रण है।

3

मैं कुछ हद तक वास्तविक दुनिया परिदृश्य की नकल करने के लिए निम्नलिखित कोड के साथ आया था।

100 कार्य समानांतर में चल रहे हैं और वे मुख्य कार्यक्रम में अपनी पूर्ण स्थिति अपडेट करते हैं। मैं कार्य पूरा होने की प्रतीक्षा करने के लिए एक काउंटरडाउन लांच का उपयोग करता हूं।

import java.util.concurrent.*; 
import java.util.*; 

public class Runner { 

    // Should be replaced with Collections.synchronizedList(new ArrayList<Integer>()) 
    public List<Integer> completed = new ArrayList<Integer>(); 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     Runner r = new Runner(); 
     ExecutorService exe = Executors.newFixedThreadPool(30); 
     int tasks = 100; 
     CountDownLatch latch = new CountDownLatch(tasks); 
     for (int i = 0; i < tasks; i++) { 
      exe.submit(r.new Task(i, latch)); 
     } 
     try { 
      latch.await(); 
      System.out.println("Summary:"); 
      System.out.println("Number of tasks completed: " 
        + r.completed.size()); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     exe.shutdown(); 
    } 

    class Task implements Runnable { 

     private int id; 
     private CountDownLatch latch; 

     public Task(int id, CountDownLatch latch) { 
      this.id = id; 
      this.latch = latch; 
     } 

     public void run() { 
      Random r = new Random(); 
      try { 
       Thread.sleep(r.nextInt(5000)); //Actual work of the task 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      completed.add(id); 
      latch.countDown(); 
     } 
    } 
} 

जब मैंने आवेदन 10 बार और कम से कम 3 से 4 बार चलाया तो कार्यक्रम ने पूर्ण कार्यों की सही संख्या मुद्रित नहीं की थी। आदर्श रूप से इसे 100 प्रिंट करना चाहिए (यदि कोई अपवाद नहीं होता है)। लेकिन कुछ मामलों में यह 98, 99 आदि प्रिंटिंग कर रहा था

इस प्रकार यह साबित करता है कि ऐरेलिस्ट के समवर्ती अपडेट सही परिणाम नहीं देंगे।

यदि मैं सिंक्रनाइज़ संस्करण के साथ ArrayList को प्रतिस्थापित करता हूं, तो प्रोग्राम सही परिणाम आउटपुट करता है। मुझे बेहतर यह है, क्योंकि के रूप में

Collections.synchronizedList(new ArrayList()); 

या

new Vector(); 

synchronizedList:

0

आप ArrayList(); के बजाय इस्तेमाल कर सकते हैं 50-100% पर

  • तेजी
  • कर सकते हैं पहले से ही मौजूदा Arra के साथ काम करते हैं yList's
संबंधित मुद्दे