您专门询问了它们在内部的工作方式,因此您在这里:
无同步
private int counter;
public int getNextUniqueIndex() {
return counter++;
}
它基本上从内存中读取值,递增它并放回内存。这在单线程中有效,但如今,在多核,多CPU,多级缓存的时代,它将无法正常工作。首先,它引入了争用条件(多个线程可以同时读取值),但也引入了可见性问题。该值可能仅存储在“本地”CPU 内存(某些缓存)中,对于其他 CPU/内核(因此 - 线程)不可见。这就是为什么许多人在线程中引用变量的本地副本的原因。这是非常不安全的。考虑一下这个流行但损坏的线程停止代码:
private boolean stopped;
public void run() {
while(!stopped) {
//do some work
}
}
public void pleaseStop() {
stopped = true;
}
添加到变量中,它工作正常 - 如果任何其他线程通过方法修改变量,则保证在工作线程的循环中立即看到该更改。顺便说一句,这也不是中断线程的好方法,请参阅:如何在没有任何使用的情况下永久停止运行的线程和停止特定的java线程。volatile
stopped
stopped
pleaseStop()
while(!stopped)
AtomicInteger
private AtomicInteger counter = new AtomicInteger();
public int getNextUniqueIndex() {
return counter.getAndIncrement();
}
该类使用 CAS(比较和交换)低级 CPU 操作(无需同步!它们允许您仅在当前值等于其他值时修改特定变量(并且成功返回)。因此,当您执行它时,它实际上在循环中运行(简化的实际实现):AtomicInteger
getAndIncrement()
int current;
do {
current = get();
} while(!compareAndSet(current, current + 1));
所以基本上:阅读;尝试存储递增的值;如果不成功(该值不再等于 ),请读取并重试。在本机代码(程序集)中实现。current
compareAndSet()
volatile
无同步
private volatile int counter;
public int getNextUniqueIndex() {
return counter++;
}
此代码不正确。它修复了可见性问题(确保其他线程可以看到所做的更改),但仍具有争用条件。这已经被解释过多次:前/后增量不是原子的。volatile
counter
唯一的副作用是“刷新”缓存,以便所有其他各方都能看到最新版本的数据。在大多数情况下,这太严格了;这就是为什么不是默认的。volatile
volatile
volatile
无同步 (2)
volatile int i = 0;
void incIBy5() {
i += 5;
}
与上面相同的问题,但更糟糕的是,因为不是。争用条件仍然存在。为什么这是一个问题?例如,如果两个线程同时运行此代码,则输出可能是 或 。但是,您保证会看到更改。i
private
+ 5
+ 10
多个独立synchronized
void incIBy5() {
int temp;
synchronized(i) { temp = i }
synchronized(i) { i = temp + 5 }
}
令人惊讶的是,此代码也不正确。事实上,这是完全错误的。首先,您正在同步,即将更改(此外,它是一个原语,所以我猜您正在同步通过自动装箱创建的临时...完全没有缺陷。你也可以写:i
i
Integer
synchronized(new Object()) {
//thread-safe, SRSLy?
}
没有两个线程可以进入具有相同锁的同一块。在这种情况下(在您的代码中类似),锁对象在每次执行时都会更改,因此实际上没有任何效果。synchronized
synchronized
即使使用了最终变量 (or) 进行同步,代码仍然不正确。两个线程可以首先同步读取(在 本地具有相同的值),然后第一个线程分配一个新值(例如,从1到6),另一个线程做同样的事情(从1到6)。this
i
temp
temp
i
同步必须跨越从读取到分配值。第一次同步没有影响(读取 a 是原子的),第二次同步也是如此。在我看来,这些是正确的形式:int
void synchronized incIBy5() {
i += 5
}
void incIBy5() {
synchronized(this) {
i += 5
}
}
void incIBy5() {
synchronized(this) {
int temp = i;
i = temp + 5;
}
}