互斥同步
互斥同步(Mutual Exclusion & Synchronization
)是一种最常见也是最主要的
并发正确性保障手段。
同步是指在多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一条
(或者一些,使用信号量的时候)线程使用。
互斥是实现同步的一种手段,临界区(Critical Section
)、互斥量(Mutex
)
和信号量(Semaphore
)都是常见的互斥实现方式。
同步是目的,互斥是方法。
Java互斥同步的实现
Java
互斥同步手段就是synchronized
关键字,这是一种块结构(Block Structured
)
的同步语法。经过Javac
编译后,会在同步块的前后分别形成monitorenter
和monitorexit
这两个字节码指令。这两个字节码指令都需要一个reference
类型
的参数来指明要锁定和解锁的对象。
新的选择,java.util.concurrent.locks.Lock
接口是JDK5
提供一中全新的互斥同步手段。
重入锁(ReentrantLock
)是Lock
接口最常见的一种实现,与synchronized
一样可重入的。
ReentrantLock
比 synchronized
多的功能:
等待可中断:指当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,
改为处理其他事情,可中断特性对处理执行时间非常长的同步块很有帮助。
公平锁:指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。
synchronized
中的锁是非公平的,ReentrantLock
在默认情况下也是非公平的。不过使用了公平锁,将导致
ReentrantLock
的性能急剧下降,会明显影响吞吐量。为啥?
锁绑定多个条件:是指一个
ReentrantLock
对象可以同时绑定多个Condition
对象。
ReentrantLock
与 synchronized
的选择
JDK6
加入了对synchronized
锁的优化,synchronized
与ReentrantLock
性能基本持平。
性能不是选择的决定因素。如果需要ReentrantLock
新增加的功能,则选择ReentrantLock
。
优先使用synchronized
的原因:
足够清晰,足够简单
Lock
需要确保在finally
块中释放锁,否则一旦受同步保护的代码块中抛出异常,则有可能永远不会释放持有锁。
synchronized
的话则由Java
虚拟机来确保即使出现异常,锁也会自动释放。JDK5
的ReentrantLock
性能领先synchronized
。JDK5
之后性能就持平了。而Java
虚拟机更容易对
synchronized
进行优化,因为Java
虚拟机可以在线程和对象的元数据中记录synchronized
中锁的相关信息,而
Lock
的话虚拟机很难知道哪些锁对象是由特定线程锁持有的。