मैं स्कैला और कार्यात्मक प्रोग्रामिंग के लिए अपेक्षाकृत नया हूं, और मुझे यह विचार पसंद है कि अपरिवर्तनीय वस्तुओं का उपयोग करके मैं कई थ्रेड सुरक्षा त्रुटियों से बच सकता हूं। एक चीज अभी भी मुझे परेशान करती है, और यह शास्त्रीय उदाहरण है जो थ्रेड सुरक्षा - साझा काउंटर को पढ़ाने के लिए प्रयोग किया जाता है।थ्रेड सुरक्षित साझा काउंटर को लागू करने के लिए कार्यात्मक तरीका
मैं सोच रहा था कि क्या थ्रेड-सुरक्षित काउंटर (इस उदाहरण में एक अनुरोध काउंटर) लागू करना संभव होगा, अपरिवर्तनीय वस्तुओं का उपयोग करके, और कार्यात्मक अवधारणाओं का उपयोग करना, और सिंक्रनाइज़ेशन से पूरी तरह से बचें।
: संदर्भ के लिएतो यहाँ पहले काउंटर के शास्त्रीय परिवर्तनशील संस्करणों (मुझे सार्वजनिक सदस्य चर के लिए बहाना, बस उदाहरण के संक्षिप्तता के लिए)
परिवर्त्य, गैर धागा सुरक्षित संस्करण हैं
public class Servlet extends HttpServlet { public int requestCount = 0; @Override public void service(ServletRequest req, ServletResponse res) throws ... { requestCount++; //thread unsafe super.service(req, res); } }
परिवर्त्य, क्लासिक धागा सुरक्षित संस्करण: (या तो मुझे आशा है कि ...)
public class Servlet extends HttpServlet {
public volatile int requestCount = 0;
@Override
public void service(ServletRequest req, ServletResponse res) throws ... {
synchronized (this) {
requestCount++;
}
super.service(req, res);
}
}
मैं सोच रहा था कि सिंक्रनाइज़ेशन के बिना थ्रेड सुरक्षा प्राप्त करने के लिए अपरिवर्तनीय ऑब्जेक्ट्स और अस्थिर चर का उपयोग करने का कोई तरीका है या नहीं।
तो यहां मेरा निष्पक्ष प्रयास था। विचार है कि काउंटर के लिए एक अपरिवर्तनीय वस्तु हो, और अस्थिर चर का उपयोग करके संदर्भ को प्रतिस्थापित करें। फिश लग रहा है, लेकिन एक शॉट के लायक है।
धारक:
public class Incrementer {
private final int value;
public Incrementer(final int oldValue) {
this.value = oldValue + 1;
}
public Incrementer() {
this.value = 0;
}
public int getValue() {
return value;
}
}
संशोधित सर्वलेट:
public class Servlet extends HttpServlet {
public volatile Incrementer incrementer = new Incrementer();
@Override
public void service(ServletRequest req, ServletResponse res) throws ... {
incrementer = new Incrementer(incrementer.getValue());
super.service(req, res);
}
}
मैं एक मजबूत लग रहा है यह भी सुरक्षित थ्रेड नहीं है, जैसा कि मैंने incrementer से पढ़ रहा हूँ, और मिल सकता है एक पुराना मूल्य (उदाहरण के लिए यदि संदर्भ पहले से ही किसी अन्य धागे द्वारा प्रतिस्थापित किया गया था)। यदि यह वास्तव में धागा सुरक्षित नहीं है, तो मुझे आश्चर्य है कि लॉकिंग/सिंक्रनाइज़ेशन के बिना ऐसे काउंटर परिदृश्य को संभालने के लिए किसी भी "कार्यात्मक" तरीके से क्या है।
तो मेरे सवाल (रों)
- है इस सूत्र किसी भी संयोग से सुरक्षित हैं?
- यदि हां, क्यों?
- यदि नहीं, तो सिंक्रनाइज़ किए बिना ऐसे काउंटर को लागू करने के लिए किसी भी तरह से है?
हालांकि उदाहरण कोड ऊपर जावा में है, स्काला में उत्तर भी स्वागत
दिलचस्प, मुझे यकीन था कि परमाणु इंटेगर आंतरिक रूप से सिंक्रनाइज़ेशन का उपयोग करता है इसलिए मैंने इसे उत्तर के रूप में भी नहीं माना, लेकिन स्रोत कोड को देखते हुए ऐसा लगता है कि यह लक्ष्य प्राप्त करने के लिए कुछ अन्य तरीकों (मूल कोड) का उपयोग कर रहा है: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/atomic/AtomicInteger.java –