2010-10-15 34 views
5

में सूची तक पहुंच के लिए सांद्रता नियंत्रण मेरे पास एक बहु-थ्रेडेड एप्लिकेशन है जिसमें एक केंद्रीकृत सूची है जिसे केवल मुख्य धागे द्वारा अपडेट किया गया है (लिखा गया है)। इसके बाद मेरे पास कई अन्य धागे हैं जिन्हें समय-समय पर वर्तमान स्थिति में सूची पुनर्प्राप्त करने की आवश्यकता है। क्या कोई ऐसी विधि है जो मुझे ऐसा करने की अनुमति दे सकती है?जावा: जावा

+0

जेडीके का आप किस संस्करण का उपयोग कर रहे हैं? यदि 1.5+ तो कुछ डिफ़ॉल्ट डेटा संरचनाएं हैं जो आपके लिए यह करती हैं, जावाडोक देखें। –

उत्तर

12

यह इस बात पर निर्भर करता है कि आप सहमति को कैसे सीमित करना चाहते हैं। सबसे आसान तरीका शायद CopyOnWriteArrayList का उपयोग कर रहा है। जब आप इससे एक इटरेटर लेते हैं, तो इटेटरेटर दर्पण करेगा जब सूची को उस बिंदु पर देखा गया था जब इटरेटर बनाया गया था - बाद में संशोधनों को पुनरावर्तक के लिए दृश्यमान नहीं दिखाई देगा। उलझन यह है कि यह काफी सारी विवादों का सामना कर सकता है, दोष यह है कि नई वस्तुओं को जोड़ना महंगा है।

लॉकिंग का दूसरा तरीका लॉकिंग है, सबसे आसान तरीका शायद Collections.synchronizedList के साथ सूची को लपेट रहा है और पुनरावृत्ति के दौरान सूची में सिंक्रनाइज़ कर रहा है।

एक तीसरा तरीका किसी प्रकार का BlockingQueue का उपयोग कर रहा है और श्रमिकों को नए तत्व खिला सकता है।

संपादित करें: ओपी के रूप में ओपी ने कहा कि केवल एक स्नैपशॉट की आवश्यकता है, CopyOnWriteArrayList शायद सबसे अच्छा आउट ऑफ़ द बॉक्स विकल्प है।

List<Foo> originalList = Collections.synchronizedList(new ArrayList()); 

public void mainThread() { 
    while(true) 
     originalList.add(getSomething()); 
} 

public void workerThread() { 
    while(true) { 
     List<Foo> copiedList; 
     synchronized (originalList) { 
      copiedList = originalList.add(something); 
     } 
     for (Foo f : copiedList) process(f); 
    } 
} 

संपादित करें:: आओ एक वैकल्पिक सिर्फ एक synchronizedList की एक प्रति पैदा कर रही है जब traversion की जरूरत है (सस्ता जोड़ने, लेकिन महंगे पढ़ने के लिए) (के बजाय कॉपी-ऑन-राइट कॉपी-ऑन-पढ़ने के लिए) इसके बारे में सोचने के लिए, कॉपी-ऑन-पढ़ने के संस्करण थोड़ा सरलीकृत सभी synchronized ब्लॉकों से बचने के लिए कर सकते हैं:

List<Foo> originalList = Collections.synchronizedList(new ArrayList()); 

public void mainThread() { 
    while(true) 
     originalList.add(getSomething()); 
} 

public void workerThread() { 
    while(true) { 
     for (Foo f : originalList.toArray(new Foo[0])) 
      process(f); 
    } 
} 

संपादित करें 2: यहाँ एक कॉपी-ऑन-पढ़ने के लिए सूची है जो नहीं है के लिए एक सरल आवरण है किसी भी सहायक का उपयोग करें, और जो लॉकिंग में ठीक-ठीक होने की कोशिश करता है

class CopyOnReadList<T> { 

    private final List<T> items = new ArrayList<T>(); 

    public void add(T item) { 
     synchronized (items) { 
      // Add item while holding the lock. 
      items.add(item); 
     } 
    } 

    public List<T> makeSnapshot() { 
     List<T> copy = new ArrayList<T>(); 
     synchronized (items) { 
      // Make a copy while holding the lock. 
      for (T t : items) copy.add(t); 
     } 
     return copy; 
    } 

} 

// Usage: 
CopyOnReadList<String> stuff = new CopyOnReadList<String>(); 
stuff.add("hello"); 
for (String s : stuff.makeSnapshot()) 
    System.out.println(s); 

मूल रूप से, आप जब लॉक करने के लिए जब आप: संभव के रूप में (मैं जानबूझ कर यह कुछ हद तक अत्यधिक, सबऑप्टिमल की सीमा पर, प्रदर्शित करने के लिए जहां ताला की जरूरत है बनाया है)

  1. ... जोड़ने सूची में एक आइटम।
  2. ... इसकी प्रतिलिपि बनाने के लिए सूची में पुन: प्रयास करें।
+0

+1 CopyOnWriteArrayList प्रश्न में जो कुछ भी देखता है उससे जाने का तरीका है। ब्लॉकिंग क्यूई को सवाल नहीं मिला है। –

+0

+1 - अच्छा सारांश। लॉकिंग के साथ ऐसा करने का एक आसान तरीका अनुरोध पर सूची की ओ (एन) प्रतिलिपि वापस करना है। सिंक्रनाइज़ सूची दृष्टिकोण के लिए आवश्यक है कि आपकी कक्षा और सूची के सभी प्राप्तकर्ता सिंक्रनाइज़ किए गए रैपर के माध्यम से सूची तक पहुंचें; और पुनरावृत्तियों को सूची में सिंक्रनाइज़ करने की आवश्यकता होती है। –

+0

क्या मुझे इस सूची के लिए लिखने के साथ-साथ सिंक्रनाइज़ करने की आवश्यकता होगी? क्या मुझे संभवतः लिखने की प्रक्रिया का एक झुकाव मिल सकता है? – pie154

0

आप बस केवल पढ़ने के लिए स्नैपशॉट चाहते हैं और सूची बहुत बड़ी नहीं है, तो: यदि धागे कर रहे हैं केवल 'समझ'

private static List<TypeHere> getCurrentList() { 
    /* code to return the list as it currently is */ 
} 
public static List<TypeHere> snapshot() { 
    TypeHere[] objects = getCurrentList().toArray(new TypeHere[] {}); 
    return Collections.unmodifiableList(Arrays.asList(objects)); 
} 
+0

यह एक बहु थ्रेडेड एप्लिकेशन है। शायद कुछ सिंक्रनाइज़ेशन यहां उपयोगी होंगे? –

+1

'toArray()' अंतर्निहित सूची नहीं है, तो थ्रेडसेफ नहीं होने वाला है, और चूंकि यह संभवतः मनमाने ढंग से सूचियों के लिए थ्रेड-सुरक्षित रैपर वर्ग है, यह काम नहीं करेगा। वैकल्पिक रूप से, यदि आपको अंतर्निहित सूची की वांछित समवर्ती अर्थशास्त्र की आवश्यकता होती है, तो मुझे नहीं लगता कि यह रैपर क्या जोड़ रहा है। –

1

सूची तुम सिर्फ सूची का उपयोग कर के साथ सुरक्षित हैं। यदि सूची "लिखित" होने जा रही है (किसी भी तरह से बदला जाए) सूची के बजाय वेक्टर का उपयोग करें।

+0

+1 वेक्टर - सिंक्रनाइज़ सूची – tylermac

2

Java's Concurrency Package पर एक नज़र डालें। वहां कुछ ऐसा होना चाहिए जिसका आप उपयोग कर सकते हैं।

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

सूची का एक स्नैपशॉट पास करने के लिए, बस मूल सूची द्वारा पॉप्युलेट की गई एक नई सूची बनाएं।

List<Object> newList; 
synchronize (originalList) 
{ 
    newList = new ArrayList<Object>(originalList); 
} 
return newList; 

सिंक्रनाइज़ यहां लाभकारी हो सकता है या नहीं भी हो सकता है। मुझे यकीन नहीं है।

+0

बच्चे धागे को उस समय पूरी सूची के स्नैपशॉट की आवश्यकता होती है जब वे इसे पढ़ते हैं। – pie154

+0

एक ही तत्व के साथ एक नई सूची पारित करके एक स्नैपशॉट बनाया जा सकता है। एक उदाहरण के साथ अद्यतन उत्तर। – tylermac

+0

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

3

आप एक रीड-राइट लॉक तंत्र का उपयोग करने पर विचार कर सकते हैं। यदि आपका जेडीके संस्करण 1.5 या नया है, तो आप ReentrantReadWriteLock का उपयोग कर सकते हैं।

0

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

यदि आपको पूर्ण सूची इंटरफ़ेस (मुख्य रूप से इंडेक्स के माध्यम से यादृच्छिक पहुंच) की आवश्यकता नहीं है, तो आपके पास और विकल्प हैं। यदि आपका उपयोग केस कतार इंटरफ़ेस से संतुष्ट है, तो ConcurrentLinkedQueue जैसी चीजें एक उत्कृष्ट पसंद होगी। यदि आप कतार इंटरफ़ेस के साथ रह सकते हैं, तो कुछ ऐसा संभव हो जाता है:

Queue<Foo> originalList = new ConcurrentLinkedQueue<Foo>(); 

public void mainWrite() { 
    // main thread 
    originalList.add(getSomething()); // no locking needed 
} 

public void workerRead() { 
    // executed by worker threads 
    // iterate without holding the lock 
    for (Foo f: originalList) { 
     process(f); 
    } 
}