1 线程间的通信机制
(1)共享内存:在Java中使用的是共享内存的并发模型。
(2)消息传递
2 Java Memory Model
关于主内存与本地内存之间具体的交互协议,即一个变量如何从主内存拷贝到本地内存、如何从本地内存同步回主内存之类的实现细节,Java内存模型中定义了以下8种操作来完成,Java虚拟机实现时必须保证下面提及的每一种操作都是原子的、不可再分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许有例外)。
(1)lock(锁定):作用于主内存的变量,它把一个变量标识为一条线程独占的状态。
(2)unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其它线程锁定。
(3)read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的本地内存中,以便随后的load操作使用。
(4)load(载入):作用于本地内存的变量,它把read操作从主内存中得到的变量的值放入本地内存的变量副本中。
(5)use(使用):作用于本地内存的变量,它把本地内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
(6)assign(赋值):作用于本地内存的变量,它把一个从执行引擎接收到的值赋给本地内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时将会执行这个操作。
(7)store(存储):作用于本地内存的变量,它把本地内存中一个变量的值传送到主内存中,以便随后的write操作使用。
(8)write(写入):作用于主内存的变量,它把store操作从本地内存中得到的变量的值放入主内存的变量中。
3 long和double的非原子性协定
允许Java虚拟机将没有被volatile修饰的64位数据的读写操作划分为两次32位的操作来进行,即允许Java虚拟机实现选择可以不保证64位数据类型的load、store、read和write这4个操作的原子性。
如果有多个线程共享一个并未声明为volatile的long或double类型的变量,并且同时对它们进行读取和修改操作,那么某些线程可能会读取到一个既非原值,也不是其它线程修改值的代表了“半个变量”的数值。
不过这种读取到“半个变量”的情况非常罕见(在目前商用Java虚拟机中不会出现),因为Java内存模型虽然允许虚拟机不把long和double变量的读写实现成原子操作,但允许虚拟机选择把这些操作实现为具有原子性的操作,而且还“强烈建议”虚拟机这样实现。在实际开发中,目前各种平台下的商用虚拟机几乎都选择把64位数据的读写操作作为原子操作来对待,因此我们在编写代码时一般不需要把用到的long和double变量专门声明为volatile。
4 重排序
为提高程序性能,编译器和CPU可能会对没有依赖关系的数据操作进行重排序。
在单线程环境下,重排序不会改变程序的运行结果。
(1)编译器优化重排序:编译器改变代码的执行顺序。
(2)指令重排序:CPU改变机器指令的执行顺序。
(3)内存系统重排序:CPU使用缓存和读写缓冲区,这使得载入和存储操作看起来是乱序执行的。
5 Happens-before
如果一个操作happens-before另一个操作,那么第一个操作的结果对于第二个操作是可见的。
如果两个操作之间存在happens-before关系,并不代表它们在JVM的具体实现必须按照happens-before关系指定的顺序执行。如果重排序之后的执行结果,与按照happens-before关系来执行的结果一致,那么这种重排序并不非法。
(1)如果操作x和操作y位于同一个线程中,在程序顺序上操作x先于操作y,那么x happens-before y。
(2)一个对象构造方法的结尾happens-before这个对象对应的finalize方法的开始。
(3)如果操作x和随后的操作y构成同步,那么x happens-before y。
(4)如果x happens-before y、y happens-before z,那么x happens-before z。
(5)对一个监视器的解锁操作happens-before随后对这个监视器的加锁操作。
(6)对一个volatile字段的写操作happens-before随后对这个字段的读操作。
(7)调用一个线程的start方法happens-before这个线程中的所有操作。
(8)如果线程A调用了线程B的join方法,那么线程B中的所有操作happens-before线程A从线程B的join方法成功返回。
(9)一个对象的默认初始化happens-before程序对这个对象的其它所有操作。
当程序中出现两个没有happens-before关系的操作对同一数据进行访问时,我们将其称为程序中存在数据竞争。
当且仅当所有连续一致的操作都没有数据竞争时,这个程序就是正确同步的。如果一个程序是正确同步的,那么这个程序中的所有操作就会表现出连续一致性。
网友评论