यह वर्ग थ्रेड सुरक्षित क्यों नहीं है?


94
class ThreadSafeClass extends Thread
{
     private static int count = 0;

     public synchronized static void increment()
     {
         count++;
     }

     public synchronized void decrement()
     {
         count--;
     }
}

क्या कोई समझा सकता है कि उपरोक्त वर्ग सुरक्षित क्यों नहीं है?


6
मैं जावा के बारे में नहीं जानता, लेकिन ऐसा लगता है कि उनमें से प्रत्येक विधि व्यक्तिगत रूप से थ्रेड-सुरक्षित है, लेकिन आप एक ही समय में प्रत्येक विधियों में एक धागा रख सकते हैं । हो सकता है कि यदि आपके पास एक एकल विधि है जो एक बूल ( increment) लेता है तो यह थ्रेड सुरक्षित होगा। या अगर आपने कुछ लॉकिंग ऑब्जेक्ट का उपयोग किया है। जैसा कि मैंने कहा, मैं जावा के बारे में नहीं जानता - मेरी टिप्पणी सी # ज्ञान से उपजी है।
वाई हा ली


मैं जावावेरी को भी अच्छी तरह से नहीं जानता, लेकिन एक स्थिर वैरिएबल तक पहुंच को सिंक्रनाइज़ करने के लिए, synchronizedकेवल स्थैतिक तरीकों का उपयोग किया जाना चाहिए। इसलिए मेरी राय में, भले ही आप incrementविधि को हटा दें , यह अभी भी थ्रेडसेफ़ नहीं है क्योंकि दो इंस्टेंसेस (जो एक ही उदाहरण के माध्यम से केवल सिंक्रनाइज़ किए गए एक्सेस हैं) विधि को समवर्ती कॉल कर सकते हैं।
ओनूर

4
यह धागा सुरक्षित है जब तक आप कभी भी कक्षा का एक उदाहरण नहीं बनाते हैं।
बेंजामिन ग्रुएनबाम

1
आपको क्यों लगता है कि यह धागा सुरक्षित नहीं है।
रायडवल

जवाबों:


134

incrementविधि के बाद से staticयह वर्ग ऑब्जेक्ट के लिए सिंक्रनाइज़ करेगा ThreadSafeClassdecrementविधि स्थिर नहीं है और सिंक्रनाइज़ करेगा उदाहरण यह कहा करते थे पर। यानी, वे विभिन्न वस्तुओं पर सिंक्रोनाइज़ करेंगे और इस तरह दो अलग-अलग थ्रेड्स एक ही समय में विधियों को निष्पादित कर सकते हैं। चूंकि ऑपरेशन ++और --ऑपरेशन परमाणु नहीं हैं, इसलिए क्लास थ्रेड सुरक्षित नहीं है।

इसके अतिरिक्त, क्योंकि countहै static, से इसे संशोधित decrementजो एक तुल्यकालन है उदाहरण विधि असुरक्षित है, क्योंकि यह विभिन्न उदाहरणों पर कहा जा सकता है और संशोधित countतरीका है कि समवर्ती।


12
आप के बाद से, जोड़ सकते countहै staticएक उदाहरण विधि है, decrement()भले ही वहाँ था नहीं, गलत है static increment()के रूप में दो धागे आह्वान कर सकते हैं विधि, decrement()समवर्ती एक ही काउंटर संशोधित विभिन्न उदाहरणों पर।
होल्गर

1
यही कारण है कि संभवतः का उपयोग करना पसंद करने के लिए एक अच्छा कारण नहीं है synchronizedबजाय पद्धति पर संशोधक का उपयोग करके, यानी की सामान्य रूप में ब्लॉक (यहां तक कि पूरी विधि सामग्री पर) synchronized(this) { ... }और synchronized(ThreadSafeClass.class) { ... }
ब्रूनो

++और --परमाणु भी नहीं हैंvolatile intSynchronizedसाथ पढ़ने / अद्यतन / लिखने समस्या का ख्याल रखता है ++/ --है, लेकिन staticकीवर्ड है, ठीक है, कुंजी यहाँ। अच्छा उत्तर!
क्रिस क्रेफ़िस

पुन :, [एक स्थिर क्षेत्र] को संशोधित करके ... एक सिंक्रनाइज़ेशन आवृत्ति विधि गलत है : किसी आवृत्ति विधि के भीतर से स्थैतिक चर तक पहुँचने के साथ कुछ भी गलत नहीं है, और synchronizedउदाहरण विधि से इसे एक्सेस करने के साथ स्वाभाविक रूप से कुछ भी गलत नहीं है । स्थैतिक डेटा के लिए सुरक्षा प्रदान करने के लिए उदाहरण विधि पर "सिंक्रनाइज़" की उम्मीद न करें। यहां एकमात्र समस्या यह है कि आपने अपने पहले पैराग्राफ में क्या कहा है: एक ही डेटा की सुरक्षा के प्रयास में तरीके अलग-अलग तालों का उपयोग करते हैं, और निश्चित रूप से यह बिल्कुल भी सुरक्षा प्रदान नहीं करता है।
सोलोमन स्लो

23

आपके पास दो सिंक्रनाइज़ किए गए तरीके हैं, लेकिन उनमें से एक स्थिर है और दूसरा नहीं है। जब यह एक प्रकार (स्थैतिक या गैर-स्थिर) के आधार पर एक सिंक्रनाइज़ विधि तक पहुंचता है, तो एक अलग ऑब्जेक्ट लॉक हो जाएगा। स्टैटिक विधि के लिए, क्लास ऑब्जेक्ट पर एक लॉक लगाया जाएगा, जबकि नॉन-स्टैटिक ब्लॉक के लिए, विधि को चलाने वाले क्लास के इंस्टेंस पर लॉक लगाया जाएगा। क्योंकि आपके पास दो अलग-अलग लॉक ऑब्जेक्ट हैं, आपके पास दो थ्रेड हो सकते हैं जो एक ही ऑब्जेक्ट को एक साथ संशोधित करते हैं।


14

क्या कोई समझा सकता है कि उपरोक्त वर्ग सुरक्षित क्यों नहीं है?

  • increment स्टैटिक होने के नाते, सिंक्रोनाइज़ेशन क्लास में ही किया जाएगा।
  • decrementस्थिर नहीं होने के कारण, ऑब्जेक्ट इंस्टेंटेशन पर सिंक्रोनाइज़ेशन किया जाएगा, लेकिन यह स्थिर होने के नाते कुछ भी सुरक्षित नहीं करता countहै।

मैं जोड़ना चाहता हूं कि एक थ्रेड-सुरक्षित काउंटर घोषित करने के लिए, मेरा मानना ​​है कि AtomicIntegerएक आदिम इंट के बजाय सबसे सरल तरीका है ।

मुझे आपको java.util.concurrent.atomicपैकेज-जानकारी पर पुनर्निर्देशित करें ।


7

दूसरों के जवाब बहुत अच्छे हैं इसका कारण बताया गया है। मैं संक्षेप में कुछ जोड़ना चाहता हूं synchronized:

public class A {
    public synchronized void fun1() {}

    public synchronized void fun2() {}

    public void fun3() {}

    public static synchronized void fun4() {}

    public static void fun5() {}
}

A a1 = new A();

synchronizedपर fun1और fun2उदाहरण ऑब्जेक्ट स्तर पर सिंक्रनाइज़ किया गया है। synchronizedपर fun4वर्ग वस्तु स्तर पर सिंक्रनाइज़ है। जिसका मतलब है:

  1. जब 2 थ्रेड्स a1.fun1()एक ही समय में कॉल करते हैं, तो बाद वाली कॉल ब्लॉक हो जाएगी।
  2. जब थ्रेड 1 कॉल a1.fun1()और थ्रेड 2 कॉल a1.fun2()एक ही समय में होता है, तो बाद वाला कॉल ब्लॉक हो जाएगा।
  3. जब थ्रेड 1 कॉल a1.fun1()और थ्रेड 2 कॉल a1.fun3()एक ही समय में, कोई अवरोध नहीं, तो 2 विधियों को एक ही समय में निष्पादित किया जाएगा।
  4. जब थ्रेड 1 कॉल A.fun4(), यदि अन्य थ्रेड्स कॉल करते हैं A.fun4()या A.fun5()एक ही समय में, कक्षा स्तर synchronizedपर बाद की कॉल को अवरुद्ध किया जाएगा fun4
  5. जब थ्रेड 1 कॉल A.fun4(), थ्रेड 2 कॉल a1.fun1()एक ही समय में, कोई अवरोध नहीं, तो 2 विधियों को एक ही समय में निष्पादित किया जाएगा।

6
  1. decrementएक अलग चीज पर ताला लगा रहा है incrementताकि वे एक दूसरे को दौड़ने से न रोकें।
  2. कॉलिंग decrementएक उदाहरण पर फोन करने के लिए एक अलग बात है पर ताला लगा है decrementएक और उदाहरण पर है, लेकिन वे एक ही बात को प्रभावित कर रहे।

पहले का अर्थ है कि कॉल ओवरलैपिंग incrementऔर decrementजिसके परिणामस्वरूप रद्द (सही) हो सकता है, वेतन वृद्धि या गिरावट।

दूसरे का मतलब है कि decrementअलग-अलग उदाहरणों पर दो ओवरलैपिंग कॉल के परिणामस्वरूप एक डबल डेक्रिमेंट (सही) या एक एकल डिक्रीमेंट हो सकता है।


4

चूंकि दो अलग-अलग विधियां हैं, एक उदाहरण स्तर है और दूसरा वर्ग स्तर है, इसलिए आपको इसे अलग करने के लिए 2 अलग-अलग वस्तुओं पर लॉक करना होगा।


1

जैसा कि अन्य उत्तरों में बताया गया है, आपका कोड थ्रेड सुरक्षित नहीं है क्योंकि स्टैटिक विधि increment()लॉक्स क्लास मॉनीटर और नॉन-स्टैटिक विधि decrement()लॉक्स मॉनिटर।

इस कोड उदाहरण के लिए, बेहतर समाधान बिना synchronzedकीवर्ड उपयोग के मौजूद है । थ्रेड सुरक्षा प्राप्त करने के लिए आपको AtomicInteger का उपयोग करना होगा।

धागा सुरक्षित उपयोग AtomicInteger:

import java.util.concurrent.atomic.AtomicInteger;

class ThreadSafeClass extends Thread {

    private static AtomicInteger count = new AtomicInteger(0);

    public static void increment() {
        count.incrementAndGet();
    }

    public static void decrement() {
        count.decrementAndGet();
    }

    public static int value() {
        return count.get();
    }

}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.