一、ReentrantLock使用
ReentrantLock是一种可重入锁,已经获得锁的线程可以继续获取锁也就是lock+1,但是同理加锁次数与释放锁次数必须相同才是正真的释放了锁。
public class ReentrantLockTest {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
// 运行线程A
ThreadA threadA = new ThreadA("ReentrantLockThreadA");
threadA.start();
// 运行线程B
ThreadB threadB = new ThreadB("ReentrantLockThreadB");
threadB.start();
}
static class ThreadA extends Thread {
public ThreadA(String name) {
super(name);
}
public void run() {
System.out.println("进入线程 " + Thread.currentThread().getName());
// 加锁
lock.lock();
System.out.println(Thread.currentThread().getName() + " 加锁1次");
lock.lock();
System.out.println(Thread.currentThread().getName() + " 加锁2次");
// 释放锁
lock.unlock();
System.out.println(Thread.currentThread().getName() + " 释放锁1次");
lock.unlock();
System.out.println(Thread.currentThread().getName() + " 释放锁2次");
}
}
static class ThreadB extends Thread {
public ThreadB(String name) {
super(name);
}
public void run() {
System.out.println("进入线程 " + Thread.currentThread().getName());
// 加锁
lock.lock();
System.out.println(Thread.currentThread().getName() + " 加锁1次");
// 释放锁
lock.unlock();
System.out.println(Thread.currentThread().getName() + " 释放锁1次");
}
}
}
打印结果
进入线程 ReentrantLockThreadA
ReentrantLockThreadA 加锁1次
ReentrantLockThreadA 加锁2次
ReentrantLockThreadA 释放锁1次
ReentrantLockThreadA 释放锁2次
进入线程 ReentrantLockThreadB
ReentrantLockThreadB 加锁1次
ReentrantLockThreadB 释放锁1次
上述打印结果显示比较明显,先对A线程加锁两次后再释放锁两次,然后线程B在A释放锁后,继续获得锁,并执行结束。
那么如果将线程A第二次释放锁的代码删除掉,也就是这两行代码
lock.unlock();
System.out.println(Thread.currentThread().getName() + " 释放锁2次");
会发生什么?
执行结果为
进入线程 ReentrantLockThreadA
ReentrantLockThreadA 加锁1次
ReentrantLockThreadA 加锁2次
ReentrantLockThreadA 释放锁1次
进入线程 ReentrantLockThreadB
线程B永远处于等待状态,拿不到锁,因为线程A少释放了一次锁。
二、ReentrantLock源码解析
ReentrantLock实现了Lock接口
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
public void lock()
// 发生中断会抛出InterruptedException
public void lockInterruptibly()
public boolean tryLock()
public boolean tryLock(long timeout, TimeUnit unit)
public void unlock()
public Condition newCondition()
// 获取加锁的次数
public int getHoldCount()
// 当前线程是否获得了锁
public boolean isHeldByCurrentThread()
// 是否已被加锁,可能是当前线程或者其它线程加的锁
public boolean isLocked()
// 公平锁会使用,判断队列中是否有线程等待获取锁
public final boolean hasQueuedThreads()
public final boolean hasQueuedThread(Thread thread)
// 公平锁队列长度
public final int getQueueLength()
public boolean hasWaiters(Condition condition)
public int getWaitQueueLength(Condition condition)
}
重点看sync以及加锁、释放锁和部分注释的方法。ReentrantLock内部实现包含了两种锁,非公平锁(默认)和公平锁,两种锁基于Sync实现。先看下Sync实现
abstract static class Sync extends AbstractQueuedSynchronizer {
abstract void lock();
// 非公平获取锁的实现, 参数acquires表示几次加锁,一般为1
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 获取当前已加锁次数,状态0表示未加锁
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
// 当前线程获得了锁
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 设置新的加锁次数
setState(nextc);
return true;
}
return false;
}
// 释放锁
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
// 只有获得锁的线程才能释放锁
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
// 当释放锁次数和加锁次数一样时,返回true
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
}
基于Sync有两种实现非公平锁NonfairSync和公平锁FairSync,直接看下NonfairSync的实现
static final class NonfairSync extends Sync {
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
lock方法获取锁的最终实现还是tryAcquire方法获得锁。
非公平锁NonfairSync与公平锁有什么区别呢?先看下非公平锁NonfairSync的实现
static final class FairSync extends Sync {
final void lock() {
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
唯一有区别的就是这一行代码
if (!hasQueuedPredecessors() &&
也就是说,公平锁根据加锁的先后顺序将线程放到队列中,当锁被上一个线程释放后,会从队列中按照顺序,取线程给予锁权限。非公平锁就是完全看"天意",大家不停try,谁也不确定什么时候能拿到锁。