Introduction
锁的特性
锁具有两种主要特性:互斥和可见性。
- 互斥指的是一次只允许一个线程持有某个特定的锁,因此可以保证共享数据内容的一致性;
- 可见性指的是必须确保锁被释放之前对共享数据的修改,随后获得锁的另一个线程能够知道该行为。
锁的代价
锁是用来做并发最简单的方式,当然其代价也是最高的。内核态的锁的时候需要操作系统进行一次上下文切换,加锁、释放锁会导致比较多的上下文切换和调度延时,等待锁的线程会被挂起直至锁释放。在上下文切换的时候,cpu之前缓存的指令和数据都将失效,对性能有很大的损失。操作系统对多线程的锁进行判断就像两姐妹在为一个玩具在争吵,然后操作系统就是能决定他们谁能拿到玩具的父母,这是很慢的。用户态的锁虽然避免了这些问题,但是其实它们只是在没有真实的竞争时才有效。
Java在JDK1.5之前都是靠synchronized关键字保证同步的,这种通过使用一致的锁定协议来协调对共享状态的访问,可以确保无论哪个线程持有守护变量的锁,都采用独占的方式来访问这些变量,如果出现多个线程同时访问锁,那第一些线线程将被挂起,当线程恢复执行时,必须等待其它线程执行完他们的时间片以后才能被调度执行,在挂起和恢复执行过程中存在着很大的开销。锁还存在着其它一些缺点,当一个线程正在等待锁时,它不能做任何事。如果一个线程在持有锁的情况下被延迟执行,那么所有需要这个锁的线程都无法执行下去。如果被阻塞的线程优先级高,而持有锁的线程优先级低,将会导致优先级反转(Priority Inversion)。
乐观锁和悲观锁
独占锁是一种悲观锁,synchronized就是一种独占锁,它假设最坏的情况,并且只有在确保其它线程不会造成干扰的情况下执行,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。而另一个更加有效的锁就是乐观锁。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。
volatile
与锁相比,volatile变量是一和更轻量级的同步机制,因为在使用这些变量时不会发生上下文切换和线程调度等操作,但是volatile变量也存在一些局限:不能用于构建原子的复合操作,因此当一个变量依赖旧值时就不能使用volatile变量。
所有读值操作都执行一个包含获取语义的读取操作,这些方法读取由参数address引用的值,然后使得CPU高速缓存内的相应字节失效;所有写值操作都执行一个包含释放语义的写入操作,这些方法将CPU高速缓存内的字节刷到内存中,然后将address参数引用的值修改为value参数所表示的值。
volatile变量具有”lock”的可见性,却不具备原子特性。也就是说线程能够自动发现volatile变量的最新值。volatile变量可以实现线程安全,但其应用有限。使用volatile变量的主要原因在于它使用非常简单,至少比使用锁机制要简单的多;其次便是性能原因了,某些情况下,它的性能要优于锁机制。此外,volatile操作不会造成阻塞。
使用volatile来确保线程安全的前提条件:
- 对变量的写操作不依赖于当前值;
- 该变量没有包含在具有其他变量的不变式中。
Atomic Operations
- all assignments of primitive types except for long and double
- all assignments of references
- all operations of java.concurrent.Atomic* classes
- all assignments to volatile longs and doubles
常见的i++操作
如果是非原子性操作,那么i++将分为三步完成:
- 读取当前i的值;
- 对i进行自增;
- 写入i最新的值。
1 | class Counter { |
假设有两个线程分别执行 inc() 和 dec() 操作,两个线程操作时序如下:
- Thread A 读取到 count 值为 0;
- Thread B 读取到 count 值为 0;
- Thread A inc(),count变成了 1;
- Thread B dec(),变成了 -1;
- Thread A 更新 count 的值为 1;
- Thread B 更新 count 的值为 -1;
执行的结果就是 Thread B 的写入操作覆盖了 Thread A的值。
为什么long型赋值不是原子操作呢?
1 | long foo = 65465498L; |
因为在实际中,Java会分两步写入这个long变量,先写32位,再写后32位。这样就线程不安全了。如果改成下面的就线程安全了:
1 | // volatile内部已经做了synchronized |
volatile不能保证原子性
1 | class TestVolatileAtomic { |
测试输出结果:
1 | ...... |
Synchronized 确保”原子性”
1 | fun inc() { |
Atomic Class 确保”原子性”
1 | class TestVolatileAtomic { |
Reference
- http://java-latte.blogspot.com/2012/10/what-is-atomicoperation-in-java-atomic.html
- https://www.vogella.com/tutorials/JavaConcurrency/article.html
- https://cloud.tencent.com/developer/article/1152642
- http://www.cnblogs.com/Mainz/p/3546347.html
- http://www.cnblogs.com/lucifer1982/archive/2008/03/23/1116981.html