Java 线程安全计数器自己的锁实现

Java thread safe counter own lock implementation

我只是在想如何在从不同线程修改变量(例如 int)时锁定它。我不会那样使用它,我知道 Java 中的同步关键字,但我想了解它以及它的一般工作原理。

这就是我的代码:

private int i = 0;
private int lock = 0;

public void count(int lock) {
    while (true) {
        if (this.lock == 0) {
            this.lock = lock;
        }

        if (this.lock == lock) {
            i ++;
            this.lock = 0;
            return;
        }
    }
}

问题:它有效吗?这个计数器线程安全吗?如果是: synchronized 关键字是否有类似的工作?如果不是:为什么不?

编辑:我忘了提到每个线程调用具有不同锁值的计数方法。

简短的回答是 'no' 那里不能保证线程安全。

较长的答案是您需要查看 Java Memory Model 以了解原因。部分问题是不同的线程可能无法同时看到更改。内存模型仅保证写入后的读取在执行写入的同一线程中看到更新的值,除非跨越内存屏障。另一个线程可能看不到该更新,即使它在另一个线程执行了写入之后执行了读取。除非您使用 volatile 关键字描述变量。或者使用 synchronized 关键字来确保只有一个线程正在更新该值。这种行为允许 Java 运行时优化代码,也许通过内联它注意到的东西在循环中是常量,并且还可以使用 提供同步性的本地汇编语言保证,但明显更快。

问题的另一部分是,当两个更新同时发生在不同的线程上时,确保两个线程都可以使用相同结果的唯一方法是使用相应的低级汇编语言说明 - 可能称为 compareAndSet、compareAndSwap 或 testAndSet。

因此,对于您编写的代码,完全有可能采用与这种执行顺序等效的方式(其中 T1 和 T2 作为单独的线程)...

// assuming i begins at 0.
T1: if this.lock == true
T2: if this.lock == true
T1: this.lock = lock
T1: if this.lock == lock     // evaluates to true
T2: if this.lock == lock     // evaluates to true
T1: this.lock = lock
T2: this.lock = lock

它实际上更复杂,因为你不能说每条指令都是 CPU 上的一条指令。从根本上说,您可以让两个线程同时执行某种更新,所有随之而来的结果都是双倍更新或丢失更新。

更简单的答案:使用 java.util.concurrent 中提供的原子原语作为线程安全计数器。