这些初始化示例是线程安全的吗?
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 和锁。
阅读了关于@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 和锁。