4.3.1। उदाहरण: वाहन ट्रैकर प्रतिनिधि का उपयोग करना
प्रतिनिधिमंडल के एक अधिक महत्वपूर्ण उदाहरण के रूप में, आइए वाहन ट्रैकर के एक संस्करण का निर्माण करें जो एक थ्रेड-सुरक्षित वर्ग को दर्शाता है। हम, तो हम एक धागा सुरक्षित मानचित्र कार्यान्वयन के साथ शुरू, एक मानचित्र में स्थानों का संग्रहण ConcurrentHashMap
। हम MutablePoint
लिस्टिंग के स्थान पर एक अपरिवर्तनीय बिंदु वर्ग का उपयोग करके स्थान को संग्रहीत भी करते हैं , जिसे लिस्टिंग 4.6 में दिखाया गया है।
लिस्टिंग 4.6। DeutatingVehicleTracker द्वारा उपयोग किए जाने वाले अपरिवर्तनीय बिंदु वर्ग।
class Point{
public final int x, y;
public Point() {
this.x=0; this.y=0;
}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
Point
थ्रेड-सुरक्षित है क्योंकि यह अपरिवर्तनीय है। अपरिवर्तनीय मूल्यों को स्वतंत्र रूप से साझा और प्रकाशित किया जा सकता है, इसलिए हमें उन्हें वापस करते समय स्थानों को कॉपी करने की आवश्यकता नहीं है।
DelegatingVehicleTracker
4.7 को सूचीबद्ध करने में किसी भी स्पष्ट तुल्यकालन का उपयोग नहीं किया जाता है; राज्य तक सभी पहुंच का प्रबंधन किया जाता है ConcurrentHashMap
, और मानचित्र की सभी कुंजी और मूल्य अपरिवर्तनीय हैं।
लिस्टिंग 4.7। एक कॉन्ट्रैक्टहैश मैप में थ्रेड सेफ्टी को डेलिगेट करना।
public class DelegatingVehicleTracker {
private final ConcurrentMap<String, Point> locations;
private final Map<String, Point> unmodifiableMap;
public DelegatingVehicleTracker(Map<String, Point> points) {
this.locations = new ConcurrentHashMap<String, Point>(points);
this.unmodifiableMap = Collections.unmodifiableMap(locations);
}
public Map<String, Point> getLocations(){
return this.unmodifiableMap; // User cannot update point(x,y) as Point is immutable
}
public Point getLocation(String id) {
return locations.get(id);
}
public void setLocation(String id, int x, int y) {
if(locations.replace(id, new Point(x, y)) == null) {
throw new IllegalArgumentException("invalid vehicle name: " + id);
}
}
}
यदि हमने MutablePoint
पॉइंट के बजाय मूल वर्ग का उपयोग किया था , तो हम getLocations
उत्परिवर्तित स्थिति का संदर्भ देते हुए एनकैप्सुलेशन को तोड़ देंगे जो थ्रेड-सुरक्षित नहीं है। ध्यान दें कि हमने वाहन ट्रैकर वर्ग के व्यवहार को थोड़ा बदल दिया है; हालांकि मॉनिटर संस्करण ने स्थानों का एक स्नैपशॉट लौटाया है, प्रतिनिधि संस्करण वाहन स्थानों का एक बेजोड़ लेकिन "लाइव" दृश्य देता है। इसका मतलब यह है कि यदि थ्रेड ए कॉल getLocations
और थ्रेड बी बाद में कुछ बिंदुओं के स्थान को संशोधित करते हैं, तो उन परिवर्तनों को प्रतिबिंबित किया जाता है जो मैप थ्रेड ए में लौट आए हैं।
4.3.2। स्वतंत्र राज्य चर
हम थ्रेड सेफ्टी को एक से अधिक अंतर्निहित स्टेट वैरिएबल में भी सौंप सकते हैं, जब तक कि उन अंतर्निहित स्टेट वैरिएबल स्वतंत्र होते हैं, जिसका अर्थ है कि कंपोजिट क्लास कई स्टेट वैरिएबल को शामिल करने वाले किसी भी इंवेरिएंट को लागू नहीं करता है।
VisualComponent
लिस्टिंग ४.१ में एक चित्रमय घटक है जो ग्राहकों को माउस और कीस्ट्रोक घटनाओं के लिए श्रोताओं को पंजीकृत करने की अनुमति देता है। यह प्रत्येक प्रकार के पंजीकृत श्रोताओं की एक सूची रखता है, ताकि जब कोई घटना घटित हो तो उपयुक्त श्रोताओं को आमंत्रित किया जा सके। लेकिन माउस श्रोताओं और प्रमुख श्रोताओं के सेट के बीच कोई संबंध नहीं है; दो स्वतंत्र हैं, और इसलिए VisualComponent
दो अंतर्निहित थ्रेड-सुरक्षित सूचियों के लिए अपने थ्रेड सुरक्षा दायित्वों को सौंप सकते हैं।
लिस्टिंग 4.9। कई अंतर्निहित राज्य चर के लिए थ्रेडिंग सुरक्षा को डेलिगेट करना।
public class VisualComponent {
private final List<KeyListener> keyListeners
= new CopyOnWriteArrayList<KeyListener>();
private final List<MouseListener> mouseListeners
= new CopyOnWriteArrayList<MouseListener>();
public void addKeyListener(KeyListener listener) {
keyListeners.add(listener);
}
public void addMouseListener(MouseListener listener) {
mouseListeners.add(listener);
}
public void removeKeyListener(KeyListener listener) {
keyListeners.remove(listener);
}
public void removeMouseListener(MouseListener listener) {
mouseListeners.remove(listener);
}
}
VisualComponent
CopyOnWriteArrayList
प्रत्येक श्रोता सूची को संग्रहीत करने के लिए एक का उपयोग करता है ; यह श्रोता-सूचियों के प्रबंधन के लिए विशेष रूप से अनुकूल एक थ्रेड-सुरक्षित सूची कार्यान्वयन है (धारा 5.2.3 देखें)। प्रत्येक सूची थ्रेड-सुरक्षित है, और क्योंकि एक के राज्य को दूसरे के राज्य में युग्मित करने में कोई बाधा नहीं VisualComponent
है, अंतर्निहित mouseListeners
और keyListeners
वस्तुओं के लिए अपनी थ्रेड सुरक्षा जिम्मेदारियों को सौंप सकते हैं।
4.3.3। जब प्रत्यायोजन विफल हो जाता है
अधिकांश मिश्रित वर्ग उतने सरल नहीं होते हैं VisualComponent
: उनके पास ऐसे घटक होते हैं जो उनके घटक अवस्था चर से संबंधित होते हैं। NumberRange
लिस्टिंग में 4.10 AtomicIntegers
अपने राज्य का प्रबंधन करने के लिए दो का उपयोग करता है , लेकिन एक अतिरिक्त बाधा लगाता है - कि पहली संख्या दूसरी से कम या उसके बराबर हो।
लिस्टिंग 4.10। संख्या रेंज वर्ग जो पर्याप्त रूप से अपने आक्रमणकारियों की रक्षा नहीं करता है। यह मत करो।
public class NumberRange {
// INVARIANT: lower <= upper
private final AtomicInteger lower = new AtomicInteger(0);
private final AtomicInteger upper = new AtomicInteger(0);
public void setLower(int i) {
//Warning - unsafe check-then-act
if(i > upper.get()) {
throw new IllegalArgumentException(
"Can't set lower to " + i + " > upper ");
}
lower.set(i);
}
public void setUpper(int i) {
//Warning - unsafe check-then-act
if(i < lower.get()) {
throw new IllegalArgumentException(
"Can't set upper to " + i + " < lower ");
}
upper.set(i);
}
public boolean isInRange(int i){
return (i >= lower.get() && i <= upper.get());
}
}
NumberRange
है धागा सुरक्षित नहीं ; यह उस अपरिपक्व को संरक्षित नहीं करता है जो कम और ऊपरी को संकुचित करता है। setLower
और setUpper
तरीकों इस अपरिवर्तनीय का प्रयास करते हैं, लेकिन ऐसा खराब करते हैं। दोनों setLower
और setUpper
जांच तत्कालीन अधिनियम दृश्यों हैं, लेकिन वे पर्याप्त लॉकिंग का उपयोग नहीं करते उन्हें परमाणु बनाने के लिए। यदि संख्या सीमा रखती है (0, 10), और एक थ्रेड कॉल setLower(5)
करता है setUpper(4)
, जबकि दूसरा थ्रेड कॉल करता है , कुछ अशुभ समय के साथ दोनों बसने वालों में चेक पास करेंगे और दोनों संशोधन लागू होंगे। नतीजा यह है कि रेंज अब (5, 4) रखती है - एक अमान्य स्थिति । इसलिए जबकि अंतर्निहित परमाणुइंटर थ्रेड सुरक्षित हैं, समग्र वर्ग नहीं है । क्योंकि अंतर्निहित अवस्था चर lower
औरupper
स्वतंत्र नहीं हैं, NumberRange
केवल थ्रेड-सेफ्टी स्टेट वैरिएबल पर थ्रेड सेफ्टी को डेलिगेट नहीं कर सकते हैं।
NumberRange
अपने आक्रमणकारियों को बनाए रखने के लिए लॉकिंग का उपयोग करके धागे को सुरक्षित बनाया जा सकता है, जैसे कि एक सामान्य लॉक के साथ निचले और ऊपरी की रखवाली। ग्राहकों को अपने आक्रमणकारियों को कम करने से रोकने के लिए निचले और ऊपरी प्रकाशन से भी बचना चाहिए।
यदि किसी वर्ग के पास यौगिक क्रियाएं हैं, तो जैसा NumberRange
कि, प्रतिनिधिमंडल फिर से धागा सुरक्षा के लिए उपयुक्त दृष्टिकोण नहीं है। इन मामलों में, वर्ग को यह सुनिश्चित करने के लिए अपनी स्वयं की लॉकिंग प्रदान करनी चाहिए कि यौगिक क्रियाएं परमाणु हैं, जब तक कि संपूर्ण यौगिक क्रिया अंतर्निहित राज्य चर को भी सौंपी जा सकती है।
यदि एक वर्ग कई स्वतंत्र थ्रेड-सेफ स्टेट वेरिएबल्स से बना होता है और कोई भी ऑपरेशन नहीं होता है जिसमें कोई भी अमान्य स्टेट ट्रांज़िशन होता है, तो यह थ्रेड सुरक्षा को अंतर्निहित स्टेट वेरिएबल्स में सौंप सकता है।