推荐 : ReentrantLock与synchronized
1. 什么是可重入锁?
- “就是可以重新获得锁!”可重入的意思是线程可以重复获得它已经持有的锁。Java的synchronized块是可重入的。
看下面代码 :
public class Reentrant{ public synchronized outer(){ inner(); } public synchronized inner(){ //do something } }
2. ReentrantLock and synchronized的不同:
- ReentrantLock 必须显示的加锁lock(),最后必须记得unlock() 而 synchronize是内置锁,不需要显示的释放锁
- ReetrantLock 可以响应中断,可以保证在线程还未获得并且试图获得锁时如果发现线程中断,则抛出异常清除中断标记退出竞争。通过“lockInterruptibly()”
- 而synchronize不会响应中断!多线程竞争一个锁时,其余未得到锁的线程只能不停的尝试获得锁,而不能中断。即使你调用了interrupt也是没用的!
- 公平性,ReentrantLock 的构造方法可以设置一个boolean fair ,获得锁的顺序是不是和申请锁的时间的顺序是一致的 ,不允许插队,默认不公平的!
4. lock方法
/**public void lock() 获取锁。 如果该锁没有被另一个线程保持,则获取该锁并立即返回,将锁的保持计数设置为 1。 如果当前线程已经保持该锁,则将保持计数加 1(state),并且该方法立即返回。 如果该锁被另一个线程保持,则出于线程调度的目的,禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态,此时锁保持计数被设置为 1。 */
5. 可重入显示锁的简单实现
public class Lock{ boolean isLocked = false; Thread lockedBy = null; int lockedCount = 0; public synchronized void lock() throws InterruptedException{ Thread callingThread = Thread.currentThread(); while(isLocked && lockedBy != callingThread){ wait(); } isLocked = true; lockedCount++; lockedBy = callingThread; } public synchronized void unlock(){ if(Thread.curentThread() == this.lockedBy){ lockedCount--; if(lockedCount == 0){ isLocked = false; notify(); } } } ... }
6. 原理分析 :
ReentrantLock的实现依赖于java同步器框架AbstractQueuedSynchronizer(本文简称之为AQS)。AQS使用一个整型的volatile变量(命名为state)来维护同步状态,马上我们会看到,这个volatile变量是ReentrantLock内存语义实现的关键。 下面是ReentrantLock的类图(仅画出与本文相关的部分):