2010-03-28 28 views
26

Làm thế nào an toàn thread là enum trong java? Tôi đang triển khai Singleton bằng enum (theo Java hiệu quả của Bloch), tôi có nên lo lắng về an toàn luồng cho enleton enum của mình không? Có cách nào để chứng minh hoặc bác bỏ rằng đó là chủ đề an toàn không?Làm thế nào an toàn thread là enum trong java?

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

Cảm ơn

+0

Bạn có ý nghĩa gì bởi "an toàn chỉ"? Phương thức 'leaveTheBuilding' không được đồng bộ hóa, do đó, tất nhiên nó có thể được chạy bởi nhiều luồng cùng một lúc. Hay bạn đang nói về việc khởi tạo 'INSTANCE'? –

+1

Khi bạn nói "singleton", bạn có nghĩa là nó có trạng thái có thể thay đổi không? Trong trường hợp đó, bạn đang ở trên một kẻ thất bại anyway. –

+0

Chỉ cần $ 0,02 của tôi, nhưng tôi nghĩ rằng bằng cách sử dụng một enum để thực thi mô hình Singleton là obfuscation mã. Điều đó nói rằng, tôi biết Bloch cùng với nhiều người khác cho thấy nó và bình luận của tôi có thể là sủa của một con khủng long. –

Trả lời

8

Kỹ thuật này là hoàn toàn thread-safe. Một giá trị enum được đảm bảo chỉ được khởi tạo một lần, bao giờ, bởi một sợi đơn, trước khi nó được sử dụng. Tuy nhiên, tôi không chắc liệu đó là khi lớp enum được nạp hay lần đầu tiên giá trị enum được truy cập. Sử dụng kỹ thuật này thực sự an toàn hơn một chút so với các kỹ thuật khác, bởi vì thậm chí không có cách nào phản chiếu để có được bản sao thứ hai của singleton dựa trên enum của bạn.

+1

Nếu enum làm điều gì đó thực sự ngu ngốc trong intialiser tĩnh của nó (hoặc ở nơi khác), nó về mặt lý thuyết có thể là một vấn đề. (Khởi tạo tĩnh là một giai đoạn khác với việc nạp lớp - xem 3 đối số 'Class.forName'.) –

+0

vâng tôi đã cố gắng tìm một cách phản chiếu để bắt đầu một enum khác, và tôi không thể. – portoalet

+0

@Mike Làm thế nào tôi có thể tìm ra khi giá trị enum được khởi tạo, tức là khi lớp enum được nạp hoặc lần đầu tiên giá trị enum được truy cập? – portoalet

31

Như @Mike đang nói, việc tạo enum được đảm bảo là luồng an toàn. Tuy nhiên, các phương thức mà bạn thêm vào một lớp enum không mang bất kỳ đảm bảo an toàn luồng nào. Cụ thể, phương thức leaveTheBuilding có thể được thực thi, đồng thời, bởi nhiều luồng. Nếu phương pháp này có tác dụng phụ (thay đổi trạng thái của một số biến) thì bạn cần phải suy nghĩ về việc bảo vệ nó (tức là, làm cho nó synchronized) hoặc các bộ phận của chúng.

9

Tùy chỉnh Enum Definition có thể không phải là chủ đề an toàn. Ví dụ,

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(); 


     } 

} 

Đôi khi đầu ra sẽ là:

B bắt đầu

B gọi fromIntegerValue & bộ BuildFromClass và Start để ngủ

Một bắt đầu

Một gọi fromIntegerValue

Một gọi setBuildFromClass và bắt đầu ngủ

Chủ đề A: lớp java.lang.String

B đều thức dậy!

Chủ đề B: lớp java.lang.String < -Chúng tôi hy vọng java.lang.Integer

Đôi khi đầu ra sẽ là:

Một bắt đầu

Một gọi fromIntegerValue

Một gọi setBuildFromClass và bắt đầu ngủ

B bắt đầu

B gọi fromIntegerValue & setBuildFromClass và Start để ngủ

Chủ đề A: lớp java.lang.Integer < -Chúng tôi mong đợi java.lang.String

B đều thức dậy!

Thread B: lớp java.lang.Integer

1

Thêm đồng bộ tránh tình trạng không phù hợp với sự đếm.

Mã bên dưới sẽ chạy sẽ khóa độc đáo luôn được in "Một". Tuy nhiên, khi bạn nhận xét đồng bộ cũng sẽ có các giá trị khác được in.

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"); 
      } 
     }; 
    } 
} 
Các vấn đề liên quan