我刚考虑过如何在从不同线程修改变量时锁定变量(如int)。我不会那样使用它,我知道Java中的synchronized关键字,但我想了解它以及它的工作原理。
这就是我的代码:
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关键字是否是一个类似的工作?如果不是:为什么不呢?
编辑:我忘了提到每个线程使用不同的锁定值调用count方法。
答案 0 :(得分:1)
简短的回答是'不'那里的线程安全无法保证。
答案越长,您需要查看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
中提供的原子原语作为线程安全计数器。