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



在阅读了关于@Bozhao'answer的评论后,我什么时候需要在Java中使用AtomicBoolean?,我对如何使用Atomic类或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应该是线程安全的,它来自于经典的双重检查锁定Singleton实现。

样品3~5怎么样?

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

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

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

能够询问对象是否已初始化,这有那么重要吗?通常情况下,您不希望公开这种功能。

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

那么你想完成什么呢?

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

检查3.2按需初始化

最新更新