阅读了关于@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();
}
}
答案 0 :(得分:0)
根据代码,它们似乎都是线程安全的,因为它们可以防止重复实例化。
能够询问对象是否已初始化有那么重要吗?通常你不想暴露那种功能。
不使用锁的问题在于,您可以从正在进行的初始化返回,但仍需要处理尚未完全完成初始化的对象。
那么你想完成什么?
有更好的方法来处理完全不需要处理 volatile 和锁的对象初始化。