1.原子性
原子性是指:在一次或者多次操作时,要么所有操作都被执行,要么所有操作都不执行。
int count = 0; //1
count++; //2
int a = count; //3
1.线程先把count=0写入工作内存中,然后再把它写入主内存,就此赋值语句可以说是具有原子性。
2.线程从主内存中读取i的值,然后把它存入当前线程的工作内存中;线程把工作内存中的count执行加1操作;线程再把count的值写入主内存中。和上一个示例一样,虽然这三个步骤都是原子性的操作,但是合在一起就不是原子性的操作。
3.线程从主内存中读取值,然后把它存入当前线程的工作内存中;线程把工作内存中的i改为j的值,然后把i的值写入主内存中。虽然这两个步骤都是原子性的操作,但是合在一起就不是原子性的操作。
简单的读取和赋值操作是原子性的,但把一个变量赋值给另一个变量就不是原子性的了;多个原子性的操作放在一起也不是原子性的。
实现原子性
java内存模型只保证了基本读取和赋值是原子性操作,如果要实现更大范围操作的原子性,可以通过synchronized和Lock来实现。由于synchronized和Lock能够保证任一时刻只有一个线程执行该代码块,那么自然就不存在原子性问题了,从而保证了原子性。
2.可见性
可见性指的是当一个线程修改了共享变量后,其他线程能够立即得知这个修改
1.定义的所有变量都储存在主内存中
2.每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝)
3.线程对共享变量所有的操作都必须在自己的工作内存中进行,不能直接从主内存中读写(不能越级)
4.不同线程之间也无法直接访问其他线程的工作内存中的变量,线程间变量值的传递需要通过主内存来进行。(同级不能相互访问)
如何保证可见性
1.使用volatile关键字
当一个变量被volatile关键字修饰时,其他线程对该变量进行了修改后,会导致当前线程在工作内存中的变量副本失效,必须从主内存中再次获取,当前线程修改工作内存中的变量后,同时也会立刻将其修改刷新到主内存中。
2. 使用synchronized关键字
synchronized关键字能够保证同一时刻只有一个线程获得锁,然后执行同步方法或者代码块,并且确保在锁释放之前,会把变量的修改刷新到主内存中。
3. 使用Lock相关的工具类
Lock相关的工具类的lock方法能够保证同一时刻只有一个线程获得锁,然后执行同步代码块,并且确保执行Lock相关的工具类的unlock方法在之前,会把变量的修改刷新到主内存中。
3.有序性
有序性指的是程序按照代码的先后顺序执行。
如何保证有序性
Java内存模型的一个叫做先行发生(Happens-Before)的原则。如果两个操作的执行顺序无法从Happens-Before原则推到出来,那么可以对它们进行随意的重排序处理了。
Happens-Before原则:
(1)程序次序原则:一段代码在单线程中执行的结果是有序的。
(2)锁定原则:一个锁处于被锁定状态,那么必须先执行unlock操作后面才能进行lock操作。
(3)volatile变量原则:同时对volatile变量进行读写操作,写操作一定先于读操作。
(4)线程启动原则:Thread对象的start方法先于此线程的每一个动作。
(5)线程终结原则:线程中的所有操作都先于对此线程的终止检测。
(6)线程中断原则:对线程interrupt方法的调用先于被中断线程的代码检测到中断事件的发生。
(7)对象终结原则:一个对象的初始化完成先于它的finalize方法的开始。
(8)传递原则:操作A先于操作B,操作B先于操作C,那么操作A一定先于操作C。