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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 08:54:50  来源:igfitidea点击:

How thread-safe is enum in java?

javaenumssingleton

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