代码之家  ›  专栏  ›  技术社区  ›  Lydon Ch

enum在java中的线程安全性如何?

  •  29
  • Lydon Ch  · 技术社区  · 14 年前

    enum在java中的线程安全性如何? 我正在使用enum(根据Bloch的有效Java)实现一个Singleton, 有没有办法证明或反驳它是线程安全的?

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

    谢谢

    4 回复  |  直到 14 年前
        1
  •  33
  •   Itay Maman    14 年前

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

        2
  •  10
  •   Jichao Zhang    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;
           }
    }
    

    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调用fromIntegerValue&setBuildFromClass并开始睡眠

    A开始

    被称为fromIntegerValue的

    线程A:类java.lang.String

    B醒了!

    <-我们期望java.lang.Integer

    有时输出将是:

    A开始

    一个叫做setBuildFromClass并开始睡眠

    B开始

    线程A:类java.lang.Integer <

    B醒了!

        3
  •  9
  •   Mike Daniels    14 年前

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

        4
  •  2
  •   Bohdan    10 年前

    添加 同步的

    下面的代码将很好地锁定打印“一”。但是当你发表评论时 同步的

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