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