推荐 : 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的类图(仅画出与本文相关的部分):