介绍
与synchronized对比:
- ReentranLock 是SDK实现的,实现了Lock接口,可重入,可相应中断,加锁解锁需手动实现
- ReentranLock 可通过构造方法入参选择公平锁与不公平锁,默认不公平锁
- synchronized 是jvm实现的,可重入,不可相应中断,加锁解锁自动实现
使用
贴代码例子:
private final Lock rtl = new ReentrantLock();
public void test(){
try {
rtl.lock();
System.out.println("lock test");
} finally {
rtl.unlock();
}
}
源码分析
ReentranLock 的所有功能均由内部静态类Sync实现,FairSync 与 NonfairSync均继承了该类。
整体以一个int类型的state与一个队列实现,state代表了锁的状态,队列则存放了等待获取锁的线程节点。
下面以NonfairSync为例分析
lock()
-
调用sync.lock
public void lock() { sync.lock(); }
非公平锁,sync 为 NonfairSync。
-
NonfairSync的**lock()**方法
final void lock() { // 若当前state为0,则代表当前锁可获得, if (compareAndSetState(0, 1)) // 若获取成功,则将当前线程设为拥有锁的线程 setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
若当前state为0,则代表当前锁可获得,若获取成功,则将当前线程设为拥有锁的线程。
(非公平锁在lock时首先就会去尝试获取锁,而公平锁则会直接进入acquire方法,不会首先尝试获取锁,这是两者第一个区别点)
protected final boolean compareAndSetState(int expect, int update) { // 调用底层native方法 // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }
若state不为0,则进入acquire 方法
-
NonfairSync的acquire(1)
public final void acquire(int arg) { // 首先尝试获取锁 if (!tryAcquire(arg) && // 获取失败,则将当前线程加入到获取队列中 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 中断线程 selfInterrupt(); }
首先尝试获取锁,若获取失败,则加入队列中
protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }
获取锁通过检查Sync的state来判断锁状态
final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); // c = 0,尝试获取锁,(公平锁还会检查是否是队列头) if (c == 0) { if (compareAndSetState(0, acquires)) { // 设置当前线程为拥有锁线程 setExclusiveOwnerThread(current); return true; } } // 如果当前线程拥有锁,则state + 1,重入锁的实现 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; }
-
NonfairSync的 acquireQueued
若获取锁失败,则加入锁获取队列,首先以当前线程建立Node节点,addWaiter:
private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure Node pred = tail; // 如果存在尾节点,则直接将新节点插入到队尾 if (pred != null) { node.prev = pred; // 这一步可能会被其它线程先插入,导致插入失败 if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } // 若没有队列,或者队尾插入失败,则重复插入动作 enq(node); return node; } private Node enq(final Node node) { // 重复插入尾部,直到成功 for (;;) { Node t = tail; if (t == null) { // Must initialize // 创建初始节点,可能被其它线程创建 if (compareAndSetHead(new Node())) tail = head; } else { node.prev = t; // 可能被其它线程抢占尾节点 if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }
此时,已将当前线程节点加入到等待队列,尝试在队列中获取锁,acquireQueued()
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); // 如果node节点的前置节点为头节点,并且node节点获取锁成功,则结束循环 if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } // 首先检查节点的前置状态,是否需要阻塞当前线程 if (shouldParkAfterFailedAcquire(p, node) && // 阻塞当前线程 parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; if (ws == Node.SIGNAL) // 前置节点的状态为-1,当前线程可直接阻塞 return true; if (ws > 0) { // 过滤掉节点前的已经取消的节点,直到ws <= 0 do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { /* * waitStatus must be 0 or PROPAGATE. Indicate that we * need a signal, but don't park yet. Caller will need to * retry to make sure it cannot acquire before parking. */ compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }
private final boolean parkAndCheckInterrupt() { // 阻塞当前线程,比较重要的一个时机 LockSupport.park(this); return Thread.interrupted(); }
lock方法的整个过程,基本就是这样,线程由阻塞状态转为非阻塞状态,是在state为0时,头节点会唤醒下一节点的线程。
unlock() 方法后续分析