Java 原子/易失性/同步之间有什么区别?
原子/易失性/同步化如何在内部工作 以下代码块之间的区别是什么 代码1Java 原子/易失性/同步之间有什么区别?,java,multithreading,synchronization,atomic,volatile,Java,Multithreading,Synchronization,Atomic,Volatile,原子/易失性/同步化如何在内部工作 以下代码块之间的区别是什么 代码1 private int counter; public int getNextUniqueIndex() { return counter++; } 代码2 private AtomicInteger counter; public int getNextUniqueIndex() { return counter.getAndIncrement(); } 代码3 private volatile
private int counter;
public int getNextUniqueIndex() {
return counter++;
}
代码2
private AtomicInteger counter;
public int getNextUniqueIndex() {
return counter.getAndIncrement();
}
代码3
private volatile int counter;
public int getNextUniqueIndex() {
return counter++;
}
volatile
是否按以下方式工作?是
volatile int i = 0;
void incIBy5() {
i += 5;
}
相当于
Integer i = 5;
void incIBy5() {
int temp;
synchronized(i) { temp = i }
synchronized(i) { i = temp + 5 }
}
我认为两个线程不能同时进入一个同步块。。。我说得对吗?如果这是真的,原子.incrementAndGet()
在没有同步的情况下如何工作?它是线程安全的吗
内部读取和写入易失性变量/原子变量之间有什么区别?我在某篇文章中读到,线程有一个变量的本地副本-这是什么?您特别询问了变量的内部工作方式,因此您可以:
不同步
它基本上从内存中读取值,增加值,然后放回内存。它可以在单线程中工作,但在当今多核、多CPU、多级缓存的时代,它无法正常工作。首先,它引入了竞争条件(多个线程可以同时读取值),但也存在可见性问题。该值可能只存储在“本地”CPU内存(一些缓存)中,对于其他CPU/核心(以及线程)不可见。这就是为什么许多引用线程中变量的本地副本。这是非常不安全的。考虑这个流行但中断的线程停止代码:
private boolean stopped;
public void run() {
while(!stopped) {
//do some work
}
}
public void pleaseStop() {
stopped = true;
}
将volatile
添加到stopped
变量中,它工作正常-如果任何其他线程通过plesestop()
方法修改stopped
变量,则可以保证在工作线程的while(!stopped)
循环中立即看到该更改。顺便说一句,这也不是中断线程的好方法,请参见:和
AtomicInteger
AtomicInteger
类使用CAS()低级CPU操作(不需要同步!),它们允许您仅在当前值等于其他值(并且成功返回)时修改特定变量。因此,当您执行getAndIncrement()
时,它实际上在一个循环中运行(简化的实际实现):
所以基本上:阅读;尝试存储递增的值;如果未成功(该值不再等于当前值),请阅读并重试。compareAndSet()
是在本机代码(程序集)中实现的
volatile
不同步
此代码不正确。它修复了可见性问题(volatile
确保其他线程可以看到对计数器所做的更改),但仍然存在争用条件。这已经发生了多次:增量前后不是原子的
volatile
的唯一副作用是“刷新”缓存,以便所有其他方都能看到最新版本的数据。这在大多数情况下过于严格;这就是为什么volatile
不是默认值
volatile
不同步(2)
与上述问题相同,但更糟糕的是,i
不是private
。比赛条件仍然存在。为什么这是个问题?例如,如果两个线程同时运行此代码,则输出可能是+5
或+10
。但是,您肯定会看到变化
多个独立的同步的
令人惊讶的是,此代码也不正确。事实上,这是完全错误的。首先,您在将要更改的i
上进行同步(而且,i
是一个原语,因此我猜您在通过自动装箱创建的临时整数上进行同步…)完全有缺陷。你也可以写:
synchronized(new Object()) {
//thread-safe, SRSLy?
}
没有两个线程可以使用相同的锁进入相同的同步
块。在这种情况下(在代码中也是如此),锁对象在每次执行时都会更改,因此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;
}
}
您特别询问了它们的内部工作方式,因此您可以:
不同步
它基本上从内存中读取值,增加值,然后放回内存。它可以在单线程中工作,但在当今多核、多CPU、多级缓存的时代,它无法正常工作。首先,它引入了竞争条件(多个线程可以同时读取值),但也存在可见性问题。该值可能只存储在“本地”CPU内存(一些缓存)中,对于其他CPU/核心(以及线程)不可见。这就是为什么许多引用线程中变量的本地副本。这是非常不安全的。考虑这个流行但中断的线程停止代码:
private boolean stopped;
public void run() {
while(!stopped) {
//do some work
}
}
public void pleaseStop() {
stopped = true;
}
将volatile
添加到stopped
变量中,它工作正常-如果任何其他线程通过plesestop()
方法修改stopped
变量,则可以保证在工作线程的while(!stopped)
循环中立即看到该更改。顺便说一句,这也不是中断线程的好方法,请参见:和
AtomicInteger
AtomicInteger
类使用CAS()低级CPU操作(不需要同步!),它们允许您仅在当前值等于其他值(并且成功返回)时修改特定变量。因此,当您执行getAndIncrement()
时,它实际上在一个循环中运行(简化的实际实现):
private volatile int counter;
public int getNextUniqueIndex() {
return counter++;
}
volatile int i = 0;
void incIBy5() {
i += 5;
}
void incIBy5() {
int temp;
synchronized(i) { temp = i }
synchronized(i) { i = temp + 5 }
}
synchronized(new Object()) {
//thread-safe, SRSLy?
}
void synchronized incIBy5() {
i += 5
}
void incIBy5() {
synchronized(this) {
i += 5
}
}
void incIBy5() {
synchronized(this) {
int temp = i;
i = temp + 5;
}
}
private Integer i = 0;
synchronized(i) {
i++;
}