आप विशेष रूप से पूछ रहे हैं कि वे आंतरिक रूप से कैसे काम करते हैं , इसलिए यहां आप हैं:
कोई सिंक्रनाइज़ेशन नहीं
private int counter;
public int getNextUniqueIndex() {
return counter++;
}
यह मूल रूप से मेमोरी से मूल्य पढ़ता है, इसे बढ़ाता है और मेमोरी में वापस डालता है। यह सिंगल थ्रेड में काम करता है, लेकिन आजकल मल्टी-कोर, मल्टी-सीपीयू, मल्टी-लेवल कैश के युग में यह सही ढंग से काम नहीं करेगा। सबसे पहले यह दौड़ की स्थिति का परिचय देता है (कई धागे एक ही समय में मूल्य पढ़ सकते हैं), लेकिन दृश्यता की समस्याएं भी। मान केवल " स्थानीय " सीपीयू मेमोरी (कुछ कैश) में संग्रहीत किया जा सकता है और अन्य सीपीयू / कोर (और इस प्रकार - थ्रेड्स) के लिए दृश्यमान नहीं हो सकता है। यही कारण है कि कई एक धागे में एक चर की स्थानीय प्रतिलिपि को संदर्भित करते हैं । यह बहुत असुरक्षित है। इस लोकप्रिय लेकिन टूटे धागे को रोकने वाले कोड पर विचार करें:
private boolean stopped;
public void run() {
while(!stopped) {
//do some work
}
}
public void pleaseStop() {
stopped = true;
}
जोड़े volatile
को stopped
चर और यह ठीक काम करता है - किसी भी अन्य धागा संशोधित करता है, तो stopped
के माध्यम से चर pleaseStop()
विधि, यदि आप यह बदलाव तुरंत देखने के लिए धागा के काम करने में गारंटी दी जाती है while(!stopped)
पाश। BTW यह एक धागे को बाधित करने का एक अच्छा तरीका नहीं है, यह देखें: एक धागे को कैसे रोकें जो बिना किसी उपयोग के हमेशा के लिए चल रहा है और एक विशिष्ट जावा धागे को रोक रहा है ।
AtomicInteger
private AtomicInteger counter = new AtomicInteger();
public int getNextUniqueIndex() {
return counter.getAndIncrement();
}
AtomicInteger
वर्ग का उपयोग करता है कैस ( तुलना और स्वैप ) निम्न स्तर सीपीयू आपरेशन (कोई तुल्यकालन की जरूरत!) वे आप एक विशेष चर संशोधित करने के लिए केवल तभी वर्तमान मूल्य कुछ और के बराबर है (और सफलतापूर्वक लौटा दिया जाता है) की अनुमति है। इसलिए जब आप निष्पादित करते हैं तो getAndIncrement()
यह वास्तव में एक लूप में चलता है (सरलीकृत वास्तविक कार्यान्वयन):
int current;
do {
current = get();
} while(!compareAndSet(current, current + 1));
तो मूल रूप से: पढ़ा; बढ़े हुए मूल्य को संग्रहीत करने का प्रयास करें; यदि सफल नहीं है (मान अब नहीं के बराबर है current
), फिर से पढ़ें और प्रयास करें। compareAndSet()
मूल कोड (विधानसभा) में कार्यान्वित किया जाता है।
volatile
तुल्यकालन के बिना
private volatile int counter;
public int getNextUniqueIndex() {
return counter++;
}
यह कोड सही नहीं है। यह दृश्यता समस्या को ठीक करता है ( volatile
यह सुनिश्चित करता है कि अन्य थ्रेड्स किए गए परिवर्तन देख सकते हैं counter
) लेकिन फिर भी एक दौड़ की स्थिति है। यह कई बार समझाया गया है : पूर्व / पश्चात वृद्धि परमाणु नहीं है।
इसका एकमात्र साइड इफेक्ट volatile
" फ्लशिंग " कैश है, ताकि अन्य सभी पार्टियां डेटा का सबसे ताज़ा संस्करण देखें। यह ज्यादातर स्थितियों में बहुत सख्त है; यही कारण volatile
है कि डिफ़ॉल्ट नहीं है।
volatile
तुल्यकालन के बिना (2)
volatile int i = 0;
void incIBy5() {
i += 5;
}
ऊपर के रूप में एक ही समस्या है, लेकिन और भी बदतर है क्योंकि i
नहीं है private
। दौड़ की स्थिति अभी भी मौजूद है। यह एक समस्या क्यों है? यदि कहते हैं, दो धागे एक साथ इस कोड को चलाते हैं, तो आउटपुट + 5
या हो सकता है + 10
। हालाँकि, आपको परिवर्तन देखने की गारंटी है।
एकाधिक स्वतंत्र synchronized
void incIBy5() {
int temp;
synchronized(i) { temp = i }
synchronized(i) { i = temp + 5 }
}
आश्चर्य, यह कोड गलत भी है। वास्तव में, यह पूरी तरह से गलत है। सबसे पहले आप उस पर सिंक्रोनाइज़ कर रहे हैं i
, जो बदलने वाला है (इसके अलावा, i
एक आदिम है, इसलिए मुझे लगता है कि आप Integer
ऑटोबॉक्सिंग के माध्यम से बनाए गए एक अस्थायी पर सिंक्रनाइज़ कर रहे हैं ...) पूरी तरह से त्रुटिपूर्ण है। आप यह भी लिख सकते हैं:
synchronized(new Object()) {
//thread-safe, SRSLy?
}
कोई भी दो धागे एक ही लॉक से एक ही synchronized
ब्लॉक में प्रवेश नहीं कर सकते हैं । इस मामले में (और इसी तरह आपके कोड में) हर निष्पादन पर लॉक ऑब्जेक्ट बदल जाता है, इसलिए प्रभावी रूप से कोई प्रभाव नहीं पड़ता है।synchronized
भले ही आपने this
सिंक्रनाइज़ेशन के लिए अंतिम चर (या ) का उपयोग किया हो , कोड अभी भी गलत है। दो धागे पहले पढ़ सकते हैं i
करने के लिए temp
तुल्यकालिक (स्थानीय स्तर पर एक ही मूल्य होने temp
के लिए है, तो पहले प्रदान करती है एक नया मान) i
(जैसे कि, 1 से 6 तक) और अन्य एक ही बात (1 से 6 तक) करता है।
मान निर्दिष्ट करने के लिए पढ़ने से सिंक्रनाइज़ेशन को पूरा करना होगा। आपके पहले सिंक्रोनाइज़ेशन का कोई प्रभाव नहीं है (पढ़ना एक int
परमाणु है) और दूसरा भी। मेरी राय में, ये सही रूप हैं:
void synchronized incIBy5() {
i += 5
}
void incIBy5() {
synchronized(this) {
i += 5
}
}
void incIBy5() {
synchronized(this) {
int temp = i;
i = temp + 5;
}
}