并发编程-ReentrantLock加锁/解锁源码分析
1、ReentrantLock是AQS(AbstractQueuedSyhronizer)在java层面的一种具体实现
=============================以下主要为thread0加锁,thread1、thread2创建节点并入队阻塞逻辑==========================
1.1、下面使用以下代码分析多个线程获取锁的原理
private static int sum = 0; private static Lock lock = new ReentrantLock(); public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 3; i++) { Thread thread = new Thread(()->{ lock.lock(); try{ // 临界区代码 // TODO 业务逻辑:读写操作不能保证线程安全 for (int j = 0; j < 10000; j++) { sum++; } }finally { lock.unlock(); } }); thread.start(); } Thread.sleep(2000); System.out.println(sum); }
1.2、第一个线程thread0通过设置state的值为1获取到非公平锁,设置当前线程为拥有独占访问权限的线程,具体实现如下:
//非公平锁 static final class NonfairSync extends Sync { final void lock() { /** * 通过cas获取锁成功 * 在这里就是thread0获取锁成功 */ if (compareAndSetState(0, 1)) //设置当前线程为拥有独占访问权限的线程 setExclusiveOwnerThread(Thread.currentThread()); else /** * 通过cas获取锁成功 * 1、再通过cas尝试加一次锁 * 2、加锁失败,为当前线程和给定模式(SHARED,EXCLUSIVE)创建节点并使其入队。 */ acquire(1); } }
1.2、************************当thread0正在执行业务逻辑并且未释放锁之前*****************************
thread1线程开始通过cas获取锁,此时会获取锁失败,并把当前线程和给定模式(SHARED,EXCLUSIVE)创建节点放入队列中,并阻塞当前线程
//非公平锁 static final class NonfairSync extends Sync { final void lock() { if (compareAndSetState(0, 1)) ... else /** * 通过cas获取锁成功 * 1、再通过cas尝试加一次锁 * 2、加锁失败,为当前线程和给定模式(SHARED,EXCLUSIVE)创建节点并使其入队。 * 此时thread1获取锁失败,执行创建节点并入队操作 */ acquire(1); } }
1.2.1、thread1具体入队执行逻辑,并阻塞线程逻辑如下:
private Node addWaiter(Node mode) { //根据线程和模式(SHARED,EXCLUSIVE)创建节点 Node node = new Node(Thread.currentThread(), mode); //第一次创建队列队列的tail为null Node pred = tail; //当队列存在的时候,其他线程入队执行的逻辑 if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } /** * 首次创建队列并入队阻塞的逻辑 * 此时enq(node)就是thread1执行的入队逻辑 */ enq(node); return node; }
1.2.2、thread1入队之后的阻塞逻辑
/** * tryAcquire为加锁逻辑 * addWaiter为创建节点并入队逻辑 * acquireQueued为阻塞逻辑 */ public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
/** * 以独占不间断模式为已在中的线程获取队列。由条件等待方法和获取方法使用 */ final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { // 返回上一个节点,如果为null则抛出NullPointerException。前置任务不能为null时使用。空检查可以被忽略 final Node p = node.predecessor(); // 通过cas加锁,加锁成功把队列的head指向当前节点,把之前节点的指针置为null if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // GC回收之前的head节点 failed = false; return interrupted; } /* * shouldParkAfterFailedAcquire(p, node)修改节点等待状态为-1(Node.waitStatus=Node.SIGNAL) * parkAndCheckInterrupt()为阻塞逻辑,具体通过调用LockSupport.park(this),当前线程进入WAIT(无时限等待状态) */ if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
2、thread2线程开始通过cas获取锁,此时会获取锁失败,并把当前线程和给定模式(SHARED,EXCLUSIVE)创建节点放入队列中,并阻塞当前线程
与thread1不同的地方如下:
private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); Node pred = tail; /** * thread2入队逻辑,并把当前节点的插入到队列的队尾 */ if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } ... return node; }
=============================以下主要为thread0释放锁,唤醒thread1加锁逻辑==========================
3、thread0释放锁逻辑如下
public final boolean release(int arg) { /** * thread0线程释放锁逻辑 * 1、修改state状态为0 * 2、把当前拥有独占访问权限的线程置空 */ if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) //thread0线程通过LockSupport.unpark(s.thread)唤醒thread1线程逻辑 unparkSuccessor(h); return true; } return false; }
4、thread0唤醒thread1线程具体逻辑如下
/** * 唤醒节点的后续节点(如果存在)。 * @param node节点 */ private void unparkSuccessor(Node node) { //cas修改当前节点状态state为0 int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0); //获取将要被唤醒的队列里的节点 Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } //当被唤醒的线程不为空时,执行唤醒 if (s != null) LockSupport.unpark(s.thread); }
5、thread1被唤醒后执行的代码逻辑
/** * 以独占不间断模式为已在中的线程获取队列。由条件等待方法和获取方法使用 */ final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { // 返回上一个节点,如果为null则抛出NullPointerException。前置任务不能为null时使用。空检查可以被忽略 final Node p = node.predecessor(); // ==========thread1线程被唤醒,通过cas加锁,加锁成功把队列的head指向当前节点,把之前节点的指针置为null========= if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // GC回收之前的head节点 failed = false; return interrupted; } /* * shouldParkAfterFailedAcquire(p, node)修改节点等待状态为-1(Node.waitStatus=Node.SIGNAL) * parkAndCheckInterrupt()为阻塞逻辑,具体通过调用LockSupport.park(this),当前线程进入WAIT(无时限等待状态)
*/ if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }

浙公网安备 33010602011771号