java中的枚举如何线程安全?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2531873/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How thread-safe is enum in java?
提问by Don Ch
How thread-safe is enum in java? I am implementing a Singleton using enum (as per Bloch's Effective Java), should I worry at all about thread safety for my singleton enum? Is there a way to prove or disprove that it is thread safe?
java中的枚举如何线程安全?我正在使用枚举实现单例(根据 Bloch 的 Effective Java),我是否应该担心单例枚举的线程安全?有没有办法证明或反驳它是线程安全的?
// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
Thanks
谢谢
回答by Mike Daniels
This technique is absolutely thread-safe. An enum value is guaranteed to only be initialized once, ever, by a single thread, before it is used. However, I'm not sure whether it is when the enum class is loaded or the first time the enum value itself is accessed. Using this technique is actually a bit safer than other techniques, because there is not even a way with reflection to get a second copy of your enum-based singleton.
这种技术绝对是线程安全的。一个枚举值保证在使用之前只被单个线程初始化一次。但是,我不确定是在加载枚举类时还是第一次访问枚举值本身。使用这种技术实际上比其他技术更安全,因为甚至没有一种方法可以通过反射来获得基于枚举的单例的第二个副本。
回答by Itay Maman
As @Mike is saying, creation of enum is guaranteed to be thread safe. However, methods that you add to an enum class do not carry any thread safety guarantee. In particular, the method leaveTheBuilding
may be executed, concurrently, by multiple threads. If this method has side effects (changes the state of some variable) then you need to think about protecting it (i.e., make it synchronized
) or parts thereof.
正如@Mike 所说,枚举的创建保证是线程安全的。但是,您添加到枚举类的方法不带有任何线程安全保证。特别地,该方法leaveTheBuilding
可以由多个线程同时执行。如果这个方法有副作用(改变一些变量的状态),那么你需要考虑保护它(即 make it synchronized
)或其中的一部分。
回答by Jichao Zhang
Customized Enum Definition may be not thread safe. For example,
自定义枚举定义可能不是线程安全的。例如,
RoleEnum.java:
角色枚举.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:
主.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();
}
}
Sometimes the output will be:
有时输出将是:
B started
B 开始
B called fromIntegerValue&setBuildFromClass and Start to sleep
B 调用 fromIntegerValue&setBuildFromClass 并开始休眠
A started
一个开始
A called fromIntegerValue
一个叫做 fromIntegerValue
A called setBuildFromClass and start to sleep
一个叫做 setBuildFromClass 并开始睡觉
Thread A: class java.lang.String
线程 A:类 java.lang.String
B waked up!
乙醒了!
Thread B: class java.lang.String <-We expect java.lang.Integer
线程 B:class java.lang.String <-我们期望 java.lang.Integer
Sometimes the output will be:
有时输出将是:
A started
一个开始
A called fromIntegerValue
一个叫做 fromIntegerValue
A called setBuildFromClass and start to sleep
一个叫做 setBuildFromClass 并开始睡觉
B started
B 开始
B called fromIntegerValue&setBuildFromClass and Start to sleep
B 调用 fromIntegerValue&setBuildFromClass 并开始休眠
Thread A: class java.lang.Integer <-We expect java.lang.String
线程 A:class java.lang.Integer <-我们期望 java.lang.String
B waked up!
乙醒了!
Thread B: class java.lang.Integer
线程 B:类 java.lang.Integer
回答by Bohdan
Adding synchronizedavoids inconsistent state with enums.
添加同步避免了与枚举不一致的状态。
Code below will run will lock nicely alway printing "One". However when you comment out synchronizedthere will be other values printed too.
下面的代码将运行将很好地锁定并始终打印“一”。但是,当您注释掉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");
}
};
}
}