您是在专门询问它们在内部如何工作,因此这里是:
没有同步
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;
}
添加volatile
到stopped
变量,它可以正常工作-如果任何其他线程stopped
通过pleaseStop()
方法修改了变量,则可以确保在工作线程的while(!stopped)
循环中立即看到该更改。顺便说一句,这也不是中断线程的好方法,请参见:如何停止永远不运行的线程以及停止特定的Java线程。
AtomicInteger
private AtomicInteger counter = new AtomicInteger();
public int getNextUniqueIndex() {
return counter.getAndIncrement();
}
本AtomicInteger
类使用CAS(比较并交换)低级别的CPU操作(没有同步需要!)他们允许你修改某个变量只有当现值等于其他的东西(并成功返回)。因此,当您执行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
即使您使用了最终变量(或this
)进行同步,代码仍然不正确。两个线程可以首先i
进行temp
同步读取(在中具有本地相同的值temp
),然后第一个将新值分配给i
(例如,从1到6),而另一个则执行相同的操作(从1到6)。
同步必须从读取到分配值。您的第一次同步无效(读取int
是原子的),而第二次同步也无效。我认为这些是正确的形式:
void synchronized incIBy5() {
i += 5
}
void incIBy5() {
synchronized(this) {
i += 5
}
}
void incIBy5() {
synchronized(this) {
int temp = i;
i = temp + 5;
}
}