前几天并发编程群里有同窗对volatile的用法提出了疑问,恰好我记得Twitter有关实时搜索的这个PPT对这个问题解释的很清晰并有一个实际的应用场景,因而周末把这个问题摘录了一些和并发相关的内容以下:html
一个线性在执行一个操做时持有对一个资源的独占锁。(互斥)
通常用在冲突比较可能发生的场景下
尝试采用原子操做,而不须要持有锁;冲突可被检测,若是发生冲突,具备相应的重试逻辑
一般用在冲突较少发生的场景下
算法确保对线程间竞争共享资源时候,不会由于互斥而使任一线程的执行无限延迟;
若是系统整个流程的执行是无阻塞的(系统某一部分可能被短暂阻塞),这种非阻塞算法就是无锁的。
无锁算法比传统的基于锁的算法对系统的开销更小,且更容易在多核多CPU处理器上扩展;
在实时系统中能够避免锁带来的延迟;
CAS (compare and swap)或LL/SC(load linked/store conditional),以及内存屏障相关的指令常常被用在算法实现中。
若是每一个线程的执行都是无阻塞的,这种非阻塞算法就是无等待的(比无锁算法更好)
Java的内存模型并不保证一个线程能够一直以程序执行的顺序看到另外一个线程对变量的修改,除非两个线程都跨越了同一个内存屏障。(Safe publication)
一个线程内的每一个动做 happens-before 同一个线程内在代码顺序上在其后的全部动做
对一个volatile变量的读,老是能看到(任意线程)对这个volatile变量最后的写入
若是A happens-before B, B happens-before C,那 A happens-before C
class VolatileExample { int x = 0; volatile int b = 0; private void write() { x = 5; b = 1; } private void read() { int dummy = b; while (x!=5) { } } public static void main(String[] args) throws Exception { final VolatileExample example = new VolatileExample(); Thread thread1 = new Thread(new Runnable() { public void run() { example.write(); } }); Thread thread2 = new Thread(new Runnable() { public void run() { example.read(); } }); thread1.start(); thread2.start(); thread1.join(); thread2.join(); } }
x并不须要定义为volatile
, 程序里能够有须要相似x的变量,咱们只须要一个volatile变量b来确保线程a能看到线程1对x的修改:
根据happens-before的传递规则,线程1的x=5;
happens-before b=1;
; 线程2的int dummy = b;
happens-beforewhile(x!=5);
根据volatile变量规则,线程2的b=1;
happens-before int dummy=b;
根据传递性,x=5;
happens-before while(x!=5);
在JSR-133以前的旧Java内存模型中,虽然不容许volatile变量之间重排序,但旧的Java内存模型仍然会容许volatile变量与普通变量之间重排序。JSR-133则加强了volatile的内存语义:严格限制编译器(在编译器)和处理器(在运行期)对volatile变量与普通变量的重排序,确保volatile的写-读和监视器的释放-获取同样,具备相同的内存语义。
延伸阅读: JSR-133: JavaTM Memory Model and Thread Specification, The JSR-133 Cookbook for Compiler Writers