2015-05-05 14 views
12

मुझे हाल ही में एक प्रश्न पूछा गया जो मुझे रोक दिया गया।बहु-थ्रेडेड वातावरण में स्वैपिंग चर

public void swapEngine(Car a, Car b) { 
    Engine temp = a.engine; 
    a.engine = b.engine; 
    b.engine = temp; 
} 

यह थ्रेड-सुरक्षित विधि नहीं है। यदि थ्रेड 1 swapEngine(car1, car2) पर कॉल करता है और फिर थ्रेड 2 swapEngine(car1, car3) पर कॉल करता है, तो car3 के इंजन के साथ समाप्त होने के लिए यह संभव है। इस समस्या को ठीक करने का सबसे स्पष्ट तरीका synchronize विधि है।

विधि को सिंक्रनाइज़ करने से संभावित अक्षमता उत्पन्न होती है। क्या होगा यदि थ्रेड 1 swapEngine(car1, car2) और थ्रेड 2 कॉल swapEngine(car3, car4) पर कॉल करता है? इन दो धागे एक-दूसरे के साथ हस्तक्षेप नहीं कर सकते हैं। इस मामले में आदर्श स्थिति दो धागे के लिए समानांतर में इंजन को स्वैप करने के लिए होगी। विधि को सिंक्रनाइज़ करने से यह घटित होने से रोकता है।

क्या इन इंजनों को समानांतरता का लाभ उठाने के दौरान इन इंजनों को थ्रेड-सुरक्षित तरीके से स्वैप करने की कोई और तकनीक है?

संपादित करें: मेड विधि सार्वजनिक।

+2

ठीक है, आप लॉक (http://docs.oracle.com/javase/7/docs/api/ जोड़ सकता है जावा/उपयोग/समवर्ती/ताले/Lock.html) कार उदाहरणों के लिए। फिर, आप केवल उन उदाहरणों को लॉक कर सकते हैं जो स्वैपिंग में भाग लेते हैं। – mkrakhin

+2

@mkrakhin यह एक स्वैप (ए, बी) में डेडलॉक प्रवण होगा; स्वैप (बी, ए); 'स्थिति। – Kayaman

+0

@ माबा, उस मामले में एक ही कार दो दुकानों में नहीं हो सकती है :) – Kishore

उत्तर

5

टिप्पणियों के अनुसार, आप कारों को स्वयं लॉक कर सकते हैं। हालांकि, अगर कारों को हमेशा एक ही क्रम में बंद नहीं किया जाता है, तो यह एक डेडलॉक का कारण बन सकता है।

तो, अगर कारों एक अद्वितीय पहचानकर्ता है, तो आप बस कारों सॉर्ट कर सकते हैं, और फिर स्वैप:

void swapEngine(Car a, Car b) { 
    Comparator<Car> byId = Comparator.comparing(Car::id); 
    Car[] cars = new Car[] {a, b}; 
    Arrays.sort(cars, byId); 
    doSwap(cars[0]), cars[1]; 
} 

private void doSwap(Car a, Car b) { 
    synchronized(a) { 
     synchronized(b) { 
      Engine temp = a.engine; 
      a.engine = b.engine; 
      b.engine = temp; 
     } 
    } 
} 

कारों किसी भी अद्वितीय उनकी तुलना करने के लिए अनुमति आईडी नहीं है, तो आप उन्हें सॉर्ट कर सकते हैं उनकी पहचान हैशकोड द्वारा (System.identityHashCode(car) का उपयोग करके प्राप्त)। यह हैशकोड, जब तक कि आपके पास बड़ी स्मृति नहीं है, कारों की एक बड़ी राशि, और दुर्भाग्यपूर्ण, अद्वितीय है। यदि आप वास्तव में ऐसी स्थिति से डरते हैं, तो अमरूद में arbitrary ordering है जिसका आप उपयोग कर सकते हैं।

0

आप AtomicReference में Car.engine स्टोर, तो आप उन्हें कैस संचालन का उपयोग कर स्वैप कर सकते हैं:

public <T> void atomicSwap(AtomicReference<T> a, AtomicReference<T> b) { 
    for(;;) { 
     T aa = a.getAndSet(null); 
     if (aa != null) { 
      T bb = b.getAndSet(null); 
      if (bb != null) { 
       // this piece will be reached ONLY if BOTH `a` and `b` 
       // contained non-null (and now contain null) 
       a.set(bb); 
       b.set(aa); 
       return; 
      } else { 
       // if `b` contained null, try to restore old value of `a` 
       // to avoid deadlocking 
       a.compareAndSet(null, aa); 
      } 
     } 
    }   
} 

इस दृष्टिकोण का लाभ यह है कि यह सही वस्तु आदेश की आवश्यकता नहीं है और आंतरिक ताले का उपयोग नहीं करते है। इसे पूर्ण वस्तु को लॉक करने की भी आवश्यकता नहीं है - अन्य गुणों को समानांतर में छेड़छाड़ की जा सकती है।

नुकसान यह है कि अब null मान अवैध हैं: उनका मतलब है कि चर पर ऑपरेशन प्रगति पर है। आप null के लिए जाँच करने के लिए जब मान रही है और उन्हें कहीं भी स्थापित करने लेकिन निर्माता में की आवश्यकता होगी:

public <T> T getValue(AtomicReference<T> a) { 
    for(;;) { 
     T v = a.get(); 
     if (v != null) 
      return v; 
    } 
} 

public <T> T setValue(AtomicReference<T> a, T value) { 
    for(;;) { 
     T old = a.get(); 
     if (old != null && a.compareAndSet(old, value)) 
      return old; 
    } 
} 
संबंधित मुद्दे