以上8中操作必须满足的规则:
1)不允许出现read和load,store和write操作之一单独出现
2)不允许一个线程丢弃他的最近的assign操作
3)不允许一个线程无原因的把数据从线程的工作内存同步到主内存中
4)一个新变量只能在主内存中”诞生”,不允许在工作内存中直接使用一个未被初始化的变量
5)一个变量在同一个时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会解锁;
6)如果对一个变量执行lock操作,那将会清空工作内存中此变量的值,在执行引擎使用这个变量之前,需要重新执行load或assign操作初始化变量的值;
7)如果一个变量事先没有被lock操作锁定,那就不允许对他执行unlock操作
8)对一个变量执行unlock之前,必须先把变量同步会主内存(执行store,write);
5.3 volatile
volatile--java虚拟机提供的轻量级的同步机制。2个重要特性。1是保证此变量对所有线程可见,2是禁止指令重排序优化,
1)每次使用之前都要先刷新,执行引擎看不到不一致的情况,保证可见性;但volatile变量在各个线程的工作内存中不存在一致性问题(也可以存在不一致的情况),但java里面的运算并非原子操作,导致volatile变量的运算在并发下一样是不安全的。(java内存模型规定,load和use动作连续,store和write动作连续)
2)指令重排从硬件上来讲,指令重排序指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理。但并不是说指令任意重排,CPU需要能正确处理指令依赖情况以保障程序能得出正确的执行结果。
性能方面:volatile的读操作的性能消耗与普通变量几乎没有差别,但是写操作则可能会慢一些,因为他需要在本地代码中插入许多内存屏蔽指令来保证处理器不发生乱序执行。
安全性方面:
以下场景仍需要同步:
1)运算结果并不依赖变量的当前值,或者能够保证只有单一的线程修改变量的值;
2)变量不需要与其他的状态变量共同参与不变约束;
5.4 long和double
非原子协定,64位数据类型,load,store,read和write操作可以不保证原子性。(目前商用jvm的实现几乎都选择把64位数据的读写操作作为原子操作来对待)
5.5 原子性、可见性和有序性(总结)
原子性:read,load,assign,use,write
可见性:java内存模型是通过变量修改后将新值同步回主存,在变量读取前从主存刷新变量值这种依赖主存作为传递媒介的方式来实现可见性的。volatile,synchronized,final(this引用逃逸除外;)
有序性:如果在本地线程内观察,所有操作都是有序的;如果在一个线程中观察另一个线程,所有的操作都是无序的;
逃逸分析:当变量(或者对象)在方法中分配后,其指针有可能被返回或者被全局引用,这样就会被其他过程或者线程所引用,这种现象称作指针(或者引用)的逃逸(Escape)。(来自互联网)
5.6 现行发生原则
指的是java内存模型中定义的两项操作之间的偏序关系,如果说操作A先行发生于操作B,其实就是说在发生操作B之前,操作A产生的影响能被操作B观察到,“影响”包括修改了内存中共享变量的值、发送了消息、调用了方法等。
java内存模型下的“天然的”先行发生关系:
1)程序次序规则:一个线程,代码顺序(控制流顺序);
2)管程锁定规则:
3)volatile变量规则:写操作先行发生于后面的读操作
4)线程启动规则:start()方法先行发生于此线程的每一个动作;
5)线程终止规则:join
7)对象终结规则;初始化先于finalize();
8)传递性;
线程状态:
1)新建new
2)运行runable
3)无期限等待waiting
4)期限等待timed waiting
5)阻塞blocked
6)结束terminated
6.线程安全
线程安全的“安全程度”有强至弱来排序:1)不变性,final
2)绝对线程安全,(在java API中标注自己是线程安全的类,大多数都不是绝对的线程安全)
3)相对线程安全,保证这个对象单独的操作时线程安全的,在调用的时候不需要做额外的保障措施,但是对于一些特定顺序的连续调用,就可能需要在调用端使用额外的同步手段来保证调用的正确性。eg:Vector,HashTable;
4)线程兼容,对象本身并不是线程安全的,但可以通过调用端正确的使用同步手段来保证对象在并发环境中可以安全的使用;eg:arraylist和hashMap;
5)线程对立
线程安全的实现方法:
1)互斥同步(阻塞同步):互斥是因,同步是果;互斥是方法,同步是目的;
java.util.concurrent.ReentranLock,比synchron增加了一些高级特性:等待可中断、可实现公平锁、以及锁可以绑定多个条件;
2)非阻塞同步:(通俗的说就是不断地重试,知道成功为止),乐观的并发策略,需要硬件指令集的支持;
常用指令集:
测试并设置Test-and-Set
获取并增加Fetch-and-Incremet
交换Swap
比较并交换CAS Compare-and-Swap ( 漏洞ABA问题)
加载连接/条件存储Load-Linked/Store-Conditional
3)无同步方案:可重入代码,线程本地存储(ThreadLocal)
锁优化
1)自旋锁与自适应自选(cas)
2)锁消除(判定依据是逃逸分析),String类,字符串相加,JDK1.5之前转化为StringBuffer类(线程安全);JDK1.5及以后,之后会StringBuilder
3)锁粗化,范围扩大
4)轻量级锁,jDK1.6加入,
5)偏向锁