一、阻塞算法与非阻塞算法
1、阻塞算法
以常见的同步实现方式synchronized
为例,同一时间段,同一个锁,只能有一个线程获得,其他未获取到的线程阻塞,直到拥有锁的线程释放锁。
下图演示了一个阻塞算法保证一个共享数据结构的行为:
2、非阻塞算法
线程A请求某种操作,如果系统无法响应;则通知A线程,A可先去执行其他操作;
下图演示了一个非阻塞算法保证一个共享数据结构的行为:
二、Volatile
变量存在的问题,
Volatile
变量写的内存语意:
当写一个Volatile
变量时,JMM会把线程对应的本地内存中的共享变量值刷新到主内存;
Volatile
变量读的内存语意:
当读一个Volatile变量时,JMM会把该线程对应的本地内存置为无效。重新主内存中读取共享变量;
还有一点需要注意:
对于任意单个volatile变量的读写具有原子性,但类似于
volatile++
这种复合操作不具有原子性;
public class SingleWriterCounter{
private volatile long count = 0;
public void inc(){
this.count++;
}
public long count(){
return this.count;
}
}
例如上面这段代码,线程A和线程B分别执行100此inc
方法,最终count
的值可能为多少?
最小值2的解解释:
①A线程执行第99次
inc
方法,但是未写入主内存,此时主内存中count
的值任为0;
②此时B线程执行第1次inc
方法,从主内存中获取到的值为0
③A线程将执行第99次inc
方法的值写入主内存,此时主内存中的count
值为99;B线程将执行第1次inc
方法的结果写入主内存,此时主内存的count
值为1
④线程A执行第100次inc
方法,从主内存中获取到的值为1 ⑤线程B执行继续执行到第100次,并将100写入主内存;
⑥线程A将第100次inc
方法结果2写入主内容
最大值200的解释:
①A线程执行100次并将结果100写入主内存;
②B线程执行100次并将结果200写入主内存;
三、Volatile
问题的解决方案
解决方案一:阻塞算法
public class SynchronizedCounter{
long count = 0;
public void inc(){
synchronized(this){
count++;
}
}
public long count(){
synchronized(this){
return this.count;
}
}
}
解决方案二:非阻塞算法
import java.util.concurrent.atomic.AtomicLong;
public class AtomicLong{
private AtomicLong count = new AtomicLong(0);
public void inc(){
boolean updated = false;
while(!updated){
long prevCount = this.count.get();
updated = this.count.compareAndSet(prevCount, prevCount + 1);
}
}
public long count(){
return this.count.get();
}
}
现代CPU广泛支持一种对内存中的共享数据进行操作的一种特殊指令(CAS),该指令的语意如下:简单的来说,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则返回V
CAS是一个原子操作,实现如下:
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(long expect, long update) {
return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
非阻塞的解决方案的通俗解释如下:
小王去买奶茶,但是有很多人都在卖,此时小王有两种解决方案:
① 排队等待(阻塞算法)
② 过段时间再来买(非阻塞算法)
关于CAS的ABA问题,请参考:http://blog.hesey.net/2011/09/resolve-aba-by-atomicstampedreference.html
参考如下:
1、非阻塞算法 并发编网
2、《Java并发编程的艺术》