这些初始化示例线程安全吗?

时间:2021-07-14 09:52:29

标签: java concurrency thread-safety atomic volatile

阅读了关于@Bozho'answer for When do I need to use AtomicBoolean in Java? 的评论后,我对如何使用原子类或 volatile 布尔值实现线程安全初始化有点困惑。

所以我写了这些示例,但不确定哪个是线程安全的?

class InitializationSample1 {
    private AtomicBoolean initialized = new AtomicBoolean(false);

    public void init(){
        if (initialized.compareAndSet(false, true)) {
            initialize();
        }
    }

    private void initialize(){}

    public boolean isInitialized(){
        return initialized.get();
    }
}

class InitializationSample2 {
    private volatile boolean initialized;

    public void init(){
        if (initialized) return;
        synchronized (this){
            if (initialized) return;

            initialize();

            initialized = true;
        }
    }

    private void initialize(){}

    public boolean isInitialized(){
        return initialized;
    }
}

class InitializationSample3 {
    private AtomicBoolean initStarted = new AtomicBoolean(false);
    private AtomicBoolean initCompleted = new AtomicBoolean(false);

    public void init(){
        if (initStarted.compareAndSet(false, true)){
            initialize();
            initCompleted.set(true);
        }
    }

    private void initialize(){}

    public boolean isInitialized(){
        return initCompleted.get();
    }
}

class InitializationSample4 {
    private AtomicInteger initialized = new AtomicInteger(0);

    public void init(){
        if (initialized.compareAndSet(0, 1)){
            initialize();
            initialized.set(2);
        }
    }

    private void initialize(){}

    public boolean isInitialized(){
        return initialized.get() == 2;
    }
}

class InitializationSample5 {
    private volatile boolean initialized;

    private AtomicBoolean once = new AtomicBoolean(false);

    public void init(){
        if (once.compareAndSet(false, true)){
            initialize();
            initialized = true;
        }
    }

    private void initialize(){}

    public boolean isInitialized(){
        return initialized;
    }
}

我知道 Sample1 不是线程安全的,因为在初始化未完成时调用 isInitialized() 可能会返回 true。

Sample2 应该是线程安全的,它来自经典的双重检查锁定单例实现。

Sample3~5怎么样?

更新。我可能需要让我的问题更具体。假设 initialize() 方法可能会创建一些对象(并且可能还有一些 getXX() 方法来获取这些对象。),并进行一些字段分配。那么,当我通过调用 isInitialized() 方法得到 true 时,我可以从任何线程正确构造这些对象吗?那些字段赋值操作是否可见?

UPDATE. @pveentjer ,我更新了其中一个示例并添加了它的用法。

我知道在实际编程场景中使用它绝对不合适。仅供讨论。

class InitializationSample3 {
    private AtomicBoolean initStarted = new AtomicBoolean(false);
    private AtomicBoolean initCompleted = new AtomicBoolean(false);

    private Foo foo;
    private int someField;

    public void init(){
        if (initStarted.compareAndSet(false, true)){
            initialize();
            initCompleted.set(true);
        }
    }

    private void initialize(){
        foo = new Foo();
        someField = 123;
    }

    public boolean isInitialized(){
        return initCompleted.get();
    }

    public Foo getFoo(){
        return foo;
    }

    public int getSomeField(){
        return someField;
    }

    public static void main(String[] args) {
        InitializationSample3 sample = new InitializationSample3();

        // the initialization may be done when the application just starts.
        new Thread(() -> {
            sample.init();
        }).start();

        // at some point after the application started, check if it is initialized
        // and get the fields from the initialized object.
        new Thread(() -> {
            while (!sample.isInitialized()){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            // Can I get the foo object fully constructed, and the new value 
            // of someField?
            System.out.println(sample.getFoo());
            System.out.println(sample.getSomeField());
        }).start();
    }
}

1 个答案:

答案 0 :(得分:0)

根据代码,它们似乎都是线程安全的,因为它们可以防止重复实例化。

能够询问对象是否已初始化有那么重要吗?通常你不想暴露那种功能。

不使用锁的问题在于,您可以从正在进行的初始化返回,但仍需要处理尚未完全完成初始化的对象。

那么你想完成什么?

有更好的方法来处理完全不需要处理 volatile 和锁的对象初始化。

检查3.2 Initialization On Demand

相关问题