[英]How thread-safe is enum in java?
如何在java中使用枚舉的線程安全性? 我正在使用枚舉實現一個Singleton(根據Bloch的Effective Java),我應該擔心我的單例枚舉的線程安全嗎? 有沒有辦法證明或證明它是線程安全的?
// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
謝謝
正如@Mike所說,enum的創建保證是線程安全的。 但是,添加到枚舉類的方法不帶任何線程安全保證。 特別地,方法leaveTheBuilding
可以由多個線程同時執行。 如果此方法有副作用(更改某些變量的狀態),那么您需要考慮保護它(即使其synchronized
)或其部分。
自定義枚舉定義可能不是線程安全的。 例如,
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並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
這種技術絕對是線程安全的。 枚舉值保證只能在使用之前由單個線程初始化一次。 但是,我不確定是在加載枚舉類還是第一次訪問枚舉值本身時。 使用這種技術實際上比其他技術更安全一些,因為甚至沒有一種方法可以使用反射來獲得基於枚舉的單例的第二個副本。
添加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");
}
};
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.