我的线程安全类使用 AtomicBoolean 有什么问题



>我有一个简单的类,我想使用原子布尔值测试线程安全。 如果测试的线程超过 2 个,则不起作用。它在某些线程中以doSome方法抛出NullPointerExcetion:有人可以告诉我我错了什么吗?谢谢。

package jcafe.common.utils;
import java.util.concurrent.atomic.AtomicBoolean;
public class ThreadSafeTest {
    private final AtomicBoolean initialized = new AtomicBoolean(false);
    private Object lazyObject = null;
    protected void initialize() {
        if (initialized.compareAndSet(false, true)) {
            // Some other expensive init here
            // Some other expensive init here
            this.lazyObject = new Object();
        }
    }
    public void doSome() {
        initialize();
        // NullPointerException here: this.lazyObject = null
        System.out.println(this.lazyObject.toString());
    }
    public static void main(String[] args) {
        final ThreadSafeTest test = new ThreadSafeTest ();
        for (int i = 0; i < 100; i++) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    test.doSome();
                }
            });
            t.start();
        }
    }
}

您没有正确完成锁定。您确保只有 1 个线程访问 initialized 变量(因为它是原子布尔值),但一旦进行了检查,就不会再进行同步。

目前,线程 T1 进入,检查 initialized 的值并将其设置为那里的第一个。线程 T2 可能会出现,检查 initialized 的值,不要输入 if 块。但是,此时无法保证lazyObject已初始化。因此,当 T1 处于睡眠状态时(因此尚未设置lazyObject),T2 从该方法返回并尝试打印lazyObject

解决此问题的最简单方法是,不要使用原子布尔值,使方法同步并使字段不稳定。无论在何处设置值,都应在同步块中执行此操作。

public class ThreadSafeTest {
    private volatile boolean initialized = false;
    private volatile Object lazyObject = null;
    protected synchronized void initialize() {
        if (!initialized) {
            initialized = true;                
            // Some other expensive init here
            this.lazyObject = new Object();
        }
    }
    ....
}

或者,您可以在施工时设置值并使其成为最终值。如果所有字段都是不可变的,它将是线程安全的。

好吧,你需要使initialize操作原子化(执行初始化逻辑并通过单个线程创建新对象),以便其他线程可以看到它的结果。

为此,例如,您可以使用synchronized阻止其他人建议的方式,而不是原子布尔值。您也可以为此使用锁。

希望有帮助。

问题是当另一个线程当前正在初始化对象时,initialize()方法将立即返回(因为compareAndSet 将返回 false)。因此,对于某些线程,在执行 initialize 方法后不会初始化对象。

它在

单线程中运行正常,因为您创建了使用 false 初始化的AtomicBoolean实例,然后在 if 语句中首次调用compareAndSet(false, true)它将返回 true。根据文档,第一个参数是您期望的,第二个参数是更新值:

true if successful. False return indicates that the actual value was not equal to the expected value.

因此,如果第一个线程调用compareAndSet则条件将为真,它将进入成功块并进入睡眠状态。现在第二个线程将调用compareAndSet并且条件将为假,它将退出函数并在 null 上调用.toString。因为您在第一个线程进入睡眠状态的成功块中创建实例。

您的线程同时执行initialize,有些线程跳过包含 init 代码的 if 测试,因为initialized为 true。

您可以围绕initialized变量添加同步

protected void initialize() {
    synchronized(initialized) {
        if (initialized.compareAndSet(false, true)) {
            try {
                // Some other expensive init here
                Thread.sleep(2 * 1000);
            } catch (InterruptedException e) {
            }
            this.lazyObject = new Object();
        }
    }
}

最新更新