互斥同步
互斥同步(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的话虚拟机很难知道哪些锁对象是由特定线程锁持有的。