关于并发编程
在并发编程领域,有两个关键问题:线程之间的通信和同步。
主内存和工作内存
Java内存模型主要目标是定义程序中各个变量的访问权限,即将变量存储到内存和从内存中取出的过程,此处的变量不同于程序中的变量。实例字段,静态字段、构成数组对象的元素,但是不包含局部变量和方法参数,因为他们是线程私有的,不会被共享,自然不会存在竞争。
java内存模型规定了变量都存储在主内存中,每条线程都有自己的工作内存,线程的工作内存中保存了该线程使用到的变量的主内存副本拷贝;线程对变量的所有操作都必须在工作内存中完成,而不能直接读取主内存的变量;不同的线程无法访问对方线程的工作内存部分
主内存和工作内存交互
一个变量是如何从主内存拷贝到工作内存,从工作内存拷贝到主内存的呢?
java内存模型定义了8中操作,虚拟机实现时必须保证每一种操作都是原子的,不可再分割的,这8中操作分别是:
- lock
- unlock
- read
- load
- use
- assign
- store
- write
原子性、可见性、有序性
java内存模型是围绕这并发过程中如何处理原子性、可见性、有序性3个特征来建立的。
- 原子性,原子性变量操作有read、load、assign、use、store、write,基本数据类型的访问读写都是具备原子性的,如果需要更大范围的原子性保证,java内存模型提供了lock、unlock操作来满足这种需求,字节码指令monitorenter和monitorexit来隐式的使用这两个操作,反映到java代码中就是synchronized, 因此synchronized块之间的操作也是具备原子性
- 可见性:指一个线程修改了变量,另一个线程能够立即得知这个修改。volatile的特殊规则保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新,因此volatile保证了多线程操作时变量的可见性,而普通变量则不能保证这一点。
除了volatile,java还有两个关键字能实现可见性,即synchronized和final。synchronized在unlock操作之前,必须先把变量同步回主内存中(store、write),final关键字的可见性指:被final修饰的字端在构造器中一旦初始化完成,构造器没有把this的引用传递出去。 - 有序性:
本线程内,所有的操作都是有序的,指的是线程内表现为串行的语义;
如果在一个线程内观察另一个线程,所有操作都是无序的,指的是指令重排序、工作内存与主内存同步延迟;
volatile、synchronized保证了线程之间操作的有序性,volatile关键字本身包含了禁止指令重排序的操作。synchronized是一个变量同一个时刻只允许一条线程对其进行lock操作,这条规则决定了持有同一个锁的两个同步块只能串行进入