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

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

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

谢谢

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

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

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

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

有时输出将是:

B开始了

B调用fromIntegerValue&setBuildFromClass并开始进入睡眠状态

一开始

一个叫fromIntegerValue的

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

线程A:类java.lang.String

B醒了!

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

有时输出将是:

一开始

一个叫fromIntegerValue的

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

B开始了

B调用fromIntegerValue&setBuildFromClass并开始进入睡眠状态

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

B醒了!

线程B:类java.lang.Integer

添加synchronized会避免与枚举不一致的状态。

下面的代码将运行将很好地锁定打印“一”。 但是,当您注释掉同步时 ,也会打印其他值。

 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"); } }; } }