java面试JDK8 new ReentrantLock()加锁流程解析
作者:子瞻
这篇文章主要为大家介绍了java面试JDK8 new ReentrantLock()加锁流程解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
new ReentrantLock() 加锁流程
//默认执行NonfairSync.lock(); final void lock() { //cas 0 -> 1 ,如果操作成功,将当前线程设置为独占线程 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else //如果操作失败 acquire(1); }
public final void acquire(int arg) { //tryAcquire(arg)尝试获取锁,如果获取锁失败返回false,否则返回true if (!tryAcquire(arg) && //尝试获得锁,如果当前线程被中断 返回 true,否则返回false acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //获得锁失败,且当前线程的中断状态为true,则重新去尝试 selfInterrupt(); }
tryAcquire()
tryAcquire()最终默认调用ReentrantLock.NonfairSync.nonfairTryAcquire();
final boolean nonfairTryAcquire(int acquires) { //获取当前线程 final Thread current = Thread.currentThread(); //获取state值 int c = getState(); //0 代表没有线程占用锁 if (c == 0) { //cas 将 0 改为 1 if (compareAndSetState(0, acquires)) { //cas 成功之后 将独占线程改为当前线程 setExclusiveOwnerThread(current); //返回成功 return true; } } //如果有线程获取当前线程(也就是当前线程是独占线程) else if (current == getExclusiveOwnerThread()) { //state值加1 int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); //保存新的state值 setState(nextc); //返回true return true; } //否则,就返回false return false; }
AbstractQueuedSynchronizer.addWaiter(Node.EXCLUSIVE)
解析
private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); //尾节点赋值给pred; Node pred = tail; //尾节点不为空 if (pred != null) { //当前的节点prev指针指向现在的尾节点 node.prev = pred; //cas 将当前尾节点 替换为 node节点 if (compareAndSetTail(pred, node)) { //当前尾节点的next指针指向 node节点 pred.next = node; return node; } } //如果尾节点为空或者cas替换失败,则执行入队操作 enq(node); return node; } //入队操作:将当前节点设置为尾节点,并更新当前节点和替换前的尾节点的指针指向 private Node enq(final Node node) { for (;;) { //把尾节点赋值给一个变量 Node t = tail; //如果尾节点为空 if (t == null) { //新建一个节点,设置为头节点 if (compareAndSetHead(new Node())) //把头节点赋值尾节点 tail = head; } else { //如果尾节点不为空,当前节点的prev指针指向尾节点 node.prev = t; //cas 将当前节点设置为尾节点 if (compareAndSetTail(t, node)) { //替换前的尾节点的next指针指向当前节点 t.next = node; return t; } } //如果cas 替换尾节点 失败,则循环执行,直到成功为止 } }
AbstractQueuedSynchronizer.acquireQueued(addWaiter(Node.EXCLUSIVE), arg);
//已经入队的非中断线程再次尝试获取锁 final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { //获取当前节点的前一个节点信息 final Node p = node.predecessor(); //如果前一个节点信息为头节点,并且成功获得锁 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); } }
shouldParkAfterFailedAcquire(p, node)
细节如下:
//获取锁失败的线程检查并更新node中的waitStatus。如果线程应该被阻塞返回true。 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { //获取前一个节点的等待状态 int ws = pred.waitStatus; //-1,代表前一个节点被阻塞中,需要唤醒 if (ws == Node.SIGNAL) return true; if (ws > 0) { // > 0 代表前一个节点被取消,就递归往找waitStatus > 0的节点信息 do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); //找到后,将此节点的next指针指向当前节点 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. * waitStatus 必须是0 或者 -3(表示可以共享获得)。表明我们需要唤醒,但是还没有阻塞。调用者需要重试去保证在阻塞操作之前不能获取成功 */ //cas 将 前一个节点的waitStatus 改为 -1(需要唤醒) compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } //返回false return false; } private final boolean parkAndCheckInterrupt() { //阻塞当前线程 LockSupport.park(this); //当前线程被唤醒后,判断当前线程的的中断状态 //这里有一个非常重要的知识点:唤醒阻塞线程的方式 1.unpark 2.interrupt return Thread.interrupted(); }
以上就是java面试JDK8 new ReentrantLock()加锁流程解析的详细内容,更多关于java JDK8 new ReentrantLock的资料请关注脚本之家其它相关文章!