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

Are these initialization samples thread-safe?

阅读了关于@Bozho'answer When do I need to use AtomicBoolean in Java? 的评论后,我对如何使用 Atomic class 或 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() 可能 return 为真。

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

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

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

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

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

那么你想完成什么?

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

勾选3.2 Initialization On Demand