2010-03-28 15 views
26

जावा में थ्रेड-सुरक्षित कैसे है? मैं enum (ब्लोच के प्रभावी जावा के अनुसार) का उपयोग करके सिंगलटन को कार्यान्वित कर रहा हूं, क्या मुझे अपने सिंगलटन एनम के लिए थ्रेड सुरक्षा के बारे में चिंता करनी चाहिए? क्या साबित करने या अस्वीकार करने का कोई तरीका है कि यह थ्रेड सुरक्षित है?जावा में थ्रेड-सुरक्षित कैसे enum है?

// Enum singleton - the preferred approach 
public enum Elvis { 
    INSTANCE; 
    public void leaveTheBuilding() { ... } 
} 

धन्यवाद

+0

"थ्रेड-सुरक्षित" से आपका क्या मतलब है? 'LeaveThebuilding' विधि सिंक्रनाइज़ नहीं है, इसलिए निश्चित रूप से इसे कई धागे द्वारा एक साथ चलाया जा सकता है। या आप 'इंस्टेंस' शुरू करने के बारे में बात कर रहे हैं? –

+1

जब आप "सिंगलटन" कहते हैं, तो क्या आपका मतलब है कि इसमें परिवर्तनीय स्थिति है? उस स्थिति में आप वैसे भी हार गए हैं। –

+0

बस मेरा $ 0.02, लेकिन मुझे लगता है कि सिंगलटन पैटर्न को लागू करने के लिए एक enum का उपयोग कोड obfuscation है। उस ने कहा, मुझे पता है कि ब्लोच कई अन्य लोगों के साथ सुझाव देते हैं और मेरी टिप्पणी डायनासोर की भौंकना हो सकती है। –

उत्तर

8

इस तकनीक को पूरी तरह से धागा सुरक्षित है। एक enum मान का उपयोग केवल एक ही थ्रेड द्वारा, कभी भी शुरू होने से पहले शुरू किया जा सकता है। हालांकि, मुझे यकीन नहीं है कि यह तब होता है जब enum क्लास लोड हो जाती है या पहली बार enum value को एक्सेस किया जाता है। इस तकनीक का उपयोग वास्तव में अन्य तकनीकों की तुलना में थोड़ा सुरक्षित है, क्योंकि आपके एनम-आधारित सिंगलटन की दूसरी प्रति प्राप्त करने के लिए प्रतिबिंब के साथ भी कोई तरीका नहीं है।

+1

यदि enum अपने स्थिर intialiser (या अन्यत्र) में वास्तव में बेवकूफ कुछ करता है, तो यह सैद्धांतिक रूप से एक समस्या हो सकती है। (स्टेटिक प्रारंभिकरण कक्षा लोडिंग से एक अलग चरण है - 3-तर्क 'कक्षा .forName' देखें।) –

+0

हाँ मैंने एक और enum init करने के लिए एक प्रतिबिंबित तरीका खोजने की कोशिश की, और मैं नहीं कर सका। – portoalet

+0

@ माइक मैं कैसे पता लगा सकता हूं कि enum मान प्रारंभ होता है, यानी जब enum क्लास लोड हो जाती है या पहली बार enum मान का उपयोग किया जाता है? – portoalet

31

जैसा कि @ माइक कह रहा है, enum का निर्माण थ्रेड सुरक्षित होने की गारंटी है। हालांकि, एक एनम कक्षा में जो विधियां आप जोड़ते हैं उनमें कोई थ्रेड सुरक्षा गारंटी नहीं होती है। विशेष रूप से, विधि leaveTheBuilding को कई धागे द्वारा समवर्ती रूप से निष्पादित किया जा सकता है। यदि इस विधि के दुष्प्रभाव होते हैं (कुछ चर की स्थिति बदलते हैं) तो आपको इसकी सुरक्षा के बारे में सोचने की आवश्यकता है (यानी, इसे synchronized बनाएं) या इसके कुछ हिस्सों।

9

अनुकूलित एनम परिभाषा थ्रेड सुरक्षित नहीं हो सकती है। उदाहरण के लिए,

RoleEnum.java:

package com.threadsafe.bad; 

public enum RoleEnum { 
     ADMIN(1), 
     DEV(2), 
     HEAD(3); 

     private Integer value; 
     private RoleEnum(Integer role){ 
       this.value=role;   
     } 
     public static RoleEnum fromIntegerValue(Integer role){ 

       for(RoleEnum x : values()){ 
        if(x.value == role){ 
          return x; 
        } 
       } 
       return RoleEnum.HEAD;    
     } 

     Class<?> buildFromClass; 
     public void setBuildFromClass(Class<?> classType){ 
       buildFromClass=classType; 
     } 
     public Class<?> getBuildFromClass(){ 
       return this.buildFromClass; 
     } 
} 

Main.java:

package com.threadsafe.bad; 

public class Main { 

     public static void main(String[] args) { 
       // TODO Auto-generated method stub 

       Thread threadA = new Thread(){ 
        public void run(){ 
          System.out.println("A started"); 
          RoleEnum role; 
          role=RoleEnum.fromIntegerValue(1); 
          System.out.println("A called fromIntegerValue"); 
          role.setBuildFromClass(String.class); 
          System.out.println("A called setBuildFromClass and start to sleep"); 


          try { 
            Thread.sleep(10000); 
          } catch (InterruptedException e) { 
            // TODO Auto-generated catch block 
            e.printStackTrace(); 
          } 
          System.out.println("Thread A: "+role.getBuildFromClass()); 
        } 
       }; 

       Thread threadB = new Thread(){ 
        public void run(){ 
          System.out.println("B started"); 
          RoleEnum role; 
          role=RoleEnum.fromIntegerValue(1); 
          role.setBuildFromClass(Integer.class); 
          System.out.println("B called fromIntegerValue&setBuildFromClass and Start to sleep"); 
          try { 
            Thread.sleep(20000); 
          } catch (InterruptedException e) { 
            // TODO Auto-generated catch block 
            e.printStackTrace(); 
          } 
          System.out.println("B waked up!"); 

          System.out.println("Thread B: "+ role.getBuildFromClass()); 
        } 

       }; 

       threadA.start(); 
       threadB.start(); 


     } 

} 

कभी कभी उत्पादन होगा:

बी

शुरू कर दिया

बी fromIntegerValue बुलाया & सेट BuildFromClass और प्रारंभ

एक

शुरू कर दिया सो

एक fromIntegerValue

नामक एक setBuildFromClass कहा जाता है और सोने के लिए

थ्रेड एक शुरू: वर्ग java.lang.String

बी ऊपर waked!

थ्रेड बी: कक्षा java.lang.String <-हम java.lang की अपेक्षा करते हैं।पूर्णांक

कभी कभी उत्पादन होगा:

एक शुरू कर दिया

एक fromIntegerValue

एक setBuildFromClass कहा जाता है और सोने के लिए

बी

बी शुरू कर दिया बुलाया शुरू बुलाया IntegerValueसेsetBuildFromClass और सोने के लिए प्रारंभ

थ्रेड एक: कक्षा java.lang.Integer < -हम उम्मीद java.lang.String

बी ऊपर waked!

थ्रेड बी: कक्षा java.lang.Integer

1

सिंक्रनाइज़ जोड़ना enums के साथ असंगत स्थिति से बचा जाता है।

नीचे कोड नीचे चलाएगा "एक" प्रिंटिंग अच्छी तरह से लॉक होगा। हालांकि जब आप सिंक्रनाइज़ पर टिप्पणी करते हैं तो अन्य मूल्य भी मुद्रित किए जाएंगे।

import java.util.Random; 
import java.util.concurrent.atomic.AtomicInteger; 

public class TestEnum 
{ 
    public static AtomicInteger count = new AtomicInteger(1); 

    public static enum E 
    { 
     One("One"), 
     Two("Two"); 

     String s; 

     E(final String s) 
     { 
      this.s = s; 
     } 

     public void set(final String s) 
     { 
      this.s = s; 
     } 

     public String get() 
     { 
      return this.s; 
     } 
    } 

    public static void main(final String[] args) 
    { 
     doit().start(); 
     doit().start(); 
     doit().start(); 
    } 

    static Thread doit() 
    { 
     return new Thread() 
     { 
      @Override 
      public void run() 
      { 
       String name = "MyThread_" + count.getAndIncrement(); 

       System.out.println(name + " started"); 

       try 
       { 
        int i = 100; 
        while (--i >= 0) 
        { 

         synchronized (E.One) 
         { 
          System.out.println(E.One.get()); 
          E.One.set("A"); 
          Thread.sleep(new Random().nextInt(100)); 
          E.One.set("B"); 
          Thread.sleep(new Random().nextInt(100)); 
          E.One.set("C"); 
          Thread.sleep(new Random().nextInt(100)); 
          E.One.set("One"); 
          System.out.println(E.One.get()); 
         } 

        } 
       } 
       catch (InterruptedException e) 
       { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 

       System.out.println(name + " ended"); 
      } 
     }; 
    } 
} 
संबंधित मुद्दे