一.原子性
概念
:一个操作或多个操作做为一个整体,要么全部执行并且必定成功执行,要么不执行。简单理解就是程序的执行是一步到位的,而不是分布式的。
在java当中,直接的读取操作和赋值(常量)属于原子性操作。对于原本不具有原子性的操作我们可以通过synchronized关键字或者Lock接口来保证同一时间只有一个线程执行同一串代码,从而也具有了原子性。
二.有序性
概念
:程序的执行是存在一定顺序的。在Java内存模型中,为了提高性能,编译器和处理器会对程序指令做重排序。在单线程中,重排序不会影响程序的正确性,但在并发编程中,却有可能得出错误的结果。
在java当中使用volatile关键字
来保证一定的有序性。
如下表所示是JMM针对编译器制定的volatile重排序规则表:
是否能重排序 | 操作二 | 操作二 | 操作二 |
---|---|---|---|
操作一 | 普通读写 | volatile读 | volatile写 |
普通读写 | 否 | ||
volatile读 | 否 | 否 | 否 |
volatile写 | 否 | 否 |
另外也可以用synchronized关键字
或Lock接口
来保证有序性。
三.可见性
概念
:在我的理解中,可见性就是指在共享变量被某一个线程修改之后,另一个线程访问的时候能够立刻得到修改以后的值。
普通的、未加修饰的共享变量是不能保证可见性的。我们照样可以通过synchronized关键字和Lock接口来保证可见性,同样也能利用volatile实现。
当一个共享变量被volatile修饰时,就可以保证
:
1.一个线程修改任意一个共享变量后,其他任何线程再次访问该变量都将获得最新值。
2.不仅是修改以后的值对其余线程可见,修改之前的值仍然具有可见性。
JSR-133 内存模型使用happens-before原则来阐释线程之间的可见性。如果一个操作对另一个操作存在可见性,那么他们之间必定符合happens-before原则:
程序顺序规则
:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作
监视器锁规则
:一个unLock操作先行发生于后面对同一个锁的lock操作。
volatile域规则
:对一个变量的写操作先行发生于后面对这个变量的读操作
传递性规则
:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C。
在并发编程中,必须同时保证程序的原子性、有序性和可见性才能够保证程序的正确性。