参考书:《深入java虚拟机》
1. 线程安全
当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获取正确的结果,那就称这个对象是线程安全的。
2. 线程安全程度
五类:不可变、绝对线程安全、相对线程安全、线程兼容、线程对立
1. 不可变:不可变带来的安全性是最直接、最纯粹的。例:用final关键字修饰、枚举类
2. 绝对线程安全:满足最上面的定义,不管如何都不会改变结果。
3. 相对线程安全:保证这个对象单次的操作是线程安全的。java中大部分线程安全的类都是相对线程安全的,例:Vector、HashTable、、Cllections的synchronizedCollection()方法包装的集合等。
4. 线程兼容:对象本身不是线程安全的,但可以通过调用端的同步手段来保证对象安全的使用。例如线程不安全的ArrayList、HashMap等。
5. 线程对立:不管调用端是否采取了同步措施,都无法在多线程环境中并发使用代码。例:Thread类的suspend()和resume()方法、System.setIn()、System.setOut()等
线程安全的实现方法
1. 互斥同步(悲观)
(一)java自带关键字:synchronized、重量级锁
1)被synchronized修饰的同步块,在对于同一线程来说是可重入的。不会自己把自己锁死。
2)被synchronized修饰的同步块在线程执行完毕之前不会释放锁,会无条件的阻塞后面其他线程的进入。不能像lock那样强制释放锁。
(二)重入锁(ReentrantLock)
实现Lock接口:可重入,等待可中断、可实现公平锁、锁可以绑定多个条件
可以使用wait()、sleep()、notify()、notifyAll()去实现线程的等待、休眠、唤醒
2. 非阻塞同步(乐观-Lock-Free)
CAS指令:比较并交换
三个操作数:内存位置V、旧的预期值A、准备设置的新值B
CAS执行时,当且仅当V符合A时,处理器才会用B更新V的值,否则它就不执行更新。无论更新与否,都会返回V的旧值。
3. 无同步方案
可重入代码(Reentrant Code):如果一个方法的返回结果时可以预测的,只要输入了相同的数据,就能返回相同的结果,那它就满足可重入性的要求,当然就是线程安全的。
线程本地存储(Thread Local Storage):如果一段代码中所需要的数据必须与其他代码共享,那就看看这些共享数据的代码是否能保证在同一线程中执行。如果能,就把共享数据的可见范围限制在同一个线程里,这样无需同步也能保证线程之间不出现数据争取的问题。
java语言中,如果一个变量被多个变量访问,可以使用volatile关键字将它声明成“易变的”
还可以通过Threadlocal来实现线程的本地存储功能。
锁优化
适应性自旋(Adaptive Spinning)
锁消除(Lock Elimination)
锁膨胀(Lock Coarsening)
轻量级锁(Lightweight Locking)
偏向锁(Biased Locking)
未完待续...