如何在java中使用枚举的线程安全性?

Don*_* Ch 31 java singleton enums

如何在java中使用枚举的线程安全性?我正在使用枚举实现一个Singleton(根据Bloch的Effective Java),我应该担心我的单例枚举的线程安全性吗?有没有办法证明或证明它是线程安全的?

// Enum singleton - the preferred approach
public enum Elvis { 
    INSTANCE;
    public void leaveTheBuilding() { ... }
}
Run Code Online (Sandbox Code Playgroud)

谢谢

Ita*_*man 33

正如@Mike所说,enum的创建保证是线程安全的.但是,添加到枚举类的方法不带任何线程安全保证.特别地,该方法leaveTheBuilding可以由多个线程同时执行.如果这种方法有副作用(改变某些变量的状态),那么你需要考虑保护它(即制造它synchronized)或其中的一部分.


Jic*_*ang 10

自定义枚举定义可能不是线程安全的.例如,

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;
       }
}
Run Code Online (Sandbox Code Playgroud)

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


       }

}
Run Code Online (Sandbox Code Playgroud)

有时输出将是:

B开始了

B调用fromIntegerValue&setBuildFromClass并Start to sleep

一开始

一个叫fromIntegerValue的

一个名为setBuildFromClass并开始进入睡眠状态

线程A:类java.lang.String

B醒了!

线程B:类java.lang.String < - 我们期望java.lang.Integer

有时输出将是:

一开始

一个叫fromIntegerValue的

一个名为setBuildFromClass并开始进入睡眠状态

B开始了

B调用fromIntegerValue&setBuildFromClass并Start to sleep

线程A:类java.lang.Integer < - 我们期望java.lang.String

B醒了!

线程B:类java.lang.Integer


Mik*_*els 9

这种技术绝对是线程安全的.枚举值保证只能在使用之前由单个线程初始化一次.但是,我不确定是在加载枚举类还是第一次访问枚举值本身时.使用这种技术实际上比其他技术更安全,因为甚至没有一种方法可以使用反射来获得基于枚举的单例的第二个副本.