Насколько потокобезопасен enum в Java? - PullRequest
30 голосов
/ 28 марта 2010

Насколько потокобезопасен enum в java? Я реализую синглтон, используя enum (согласно Эффективной Java Блоха), я должен волноваться вообще о безопасности потока для моего единственного перечисления? Есть ли способ доказать или опровергнуть, что это потокобезопасный?

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

Спасибо

Ответы [ 4 ]

33 голосов
/ 28 марта 2010

Как говорит @Mike, создание enum гарантированно поточно-ориентировано. Однако методы, которые вы добавляете в класс enum, не несут никакой гарантии безопасности потока. В частности, способ leaveTheBuilding может выполняться одновременно несколькими потоками. Если этот метод имеет побочные эффекты (изменяет состояние некоторой переменной), вам нужно подумать о его защите (т.е. сделать его synchronized) или его частях.

10 голосов
/ 17 октября 2014

Индивидуальное определение Enum может быть не поточно-ориентированным.Например,

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

A запущен

A вызван fromIntegerValue

A вызвал setBuildFromClass и начал спать

Поток A: класс java.lang.String

B проснулся!

Поток B: класс java.lang.String <- Мы ожидаем java.lang.Integer</strong>

Иногда вывод будет:

A запущен

A вызван fromIntegerValue

A вызван setBuildFromClass и начнется сsleep

B запущен

B вызван fromIntegerValue & setBuildFromClass и Start to sleep

Поток A: класс java.lang.Integer <- мы ожидаем java.lang.String </strong>

B проснулся!

Тема B: класс java.lang.Integer

9 голосов
/ 28 марта 2010

Эта техника абсолютно поточнобезопасна. Значение enum гарантируется только один раз, когда-либо одним потоком, прежде чем оно будет использовано. Однако я не уверен, когда это происходит, когда загружается класс enum или при первом обращении к самому значению enum. Использование этой техники на самом деле немного безопаснее, чем других техник, потому что даже в рефлексии нет способа получить вторую копию вашего синглтона на основе enum.

2 голосов
/ 23 октября 2014

Добавление synchronized позволяет избежать несовместимого состояния с перечислениями.

Код, приведенный ниже, будет всегда хорошо блокировать печать «One». Однако, когда вы закомментируете синхронизированный , будут напечатаны и другие значения.

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");
            }
        };
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...