J.U.C — Locks — ReentrantLock(一)

整体结构



公平锁和非公平锁

  如果获取一个锁是按照请求的顺序得到的,那么就是公平锁,否则就是非公平锁。
     公平锁保证一个阻塞的线程最终能够获得锁,因为是有序的,所以总是可以按照请求的顺序获得锁。
     不公平锁意味着后请求锁的线程可能在其前面排列的休眠线程恢复前拿到锁,这样就有可能提高并发的性能。
     这是因为通常情况下挂起的线程重新开始与它真正开始运行,二者之间会产生严重的延时。因此非公平锁就可以利用这段时间完成操作。这是非公平锁在某些时候比公平锁性能要好的原因之一。
  • 初始化
public ReentrantLock() {
    sync = new NonfairSync();     // 默认是非公平锁
}

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
  • 公平锁 和 非公平锁 : Lock
ReentrantLock lock = new ReentrantLock(true); // 声明公平锁
lock.lock();
try {
    // method body
} finally {
     lock.unlock();
}

lock.lock() -->
              // 公平锁: ReentrantLock.FairSync
              final void lock() {
                  acquire(1);
              }
              // 非公平锁: ReentrantLock.NonfairSync(Sync默认)
              final void lock() {
                  if (compareAndSetState(0, 1))     // 一定程度上不够公平
                      setExclusiveOwnerThread(Thread.currentThread());
                  else
                      acquire(1);
              }

           
                                                -->
                                                // AQS
                                                public final void acquire(int arg) {
                                                    if (!tryAcquire(arg) &&     // 1. 如果tryAcquire(arg)成功,那就没有问题拿到锁;若失败则2
                                                         acquireQueued(addWaiter(Node.EXCLUSIVE), arg))     // 2. 创建一个独占的节点(Node.EXCLUSIVE),并插入CLH队尾;自旋获取锁
                                                         selfInterrupt();     // 3. 因为在获得锁之前线程会被中断,这个地方会清楚中断标记位
                                               }

                  -->
                  公平锁
                  1.
                  protected final boolean tryAcquire(int acquires) {
                      final Thread current = Thread.currentThread();
                      int c = getState();     // 获取当前锁的状态
                      if (c == 0) {     // 若为0,说明没有线程持有锁
                          if (!hasQueuedPredecessors() &&     // 若有先序节点,退出;若无则获取锁。 确保公平性,即前面的节点先获取锁
                              compareAndSetState(0, acquires)) {
                              setExclusiveOwnerThread(current);
                              return true;
                          }
                      }
                      else if (current == getExclusiveOwnerThread()) {     // 若持有锁的线程就是当前线程,则直接state++
                          int nextc = c + acquires;     
                          if (nextc < 0)
                              throw new Error("Maximum lock count exceeded");
                              setState(nextc);
                              return true;
                          }
                      return false;
                  }

                  非公平锁
                  1.
                  final boolean nonfairTryAcquire(int acquires) {
                      final Thread current = Thread.currentThread();
                      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;
                      }


                                             -->
                                             2.自旋
                                             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)) {     // 如果当前节点是AQS队列的头结点(如果第一个节点是DUMP节点也就是傀儡节点,那么第二个节点实际上就是头结点了),就尝试在此获取锁tryAcquire(arg)。如果成功就将头结点设置为当前节点(不管第一个结点是否是DUMP节点),返回中断位
                                                             setHead(node);
                                                             p.next = null; // help GC ?没有把node的prev指针设为null,怎么GC
                                                             failed = false;
                                                             return interrupted;
                                                         }
                                                         if (shouldParkAfterFailedAcquire(p, node) &&
                                                                                              parkAndCheckInterrupt())
                                                             interrupted = true;
                                                         }
                                                     } finally {
                                                         if (failed)
                                                             cancelAcquire(node);
                                                     }
}
                                                     Node node = new Node(Thread.currentThread(), mode);
                                                     Node pred = tail;
                                                     if (pred != null) {     // 若队列不为空,则将当前节点设为tail
                                                         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()))     // Dump Node
                                                                  tail = head;
                                                             } else {
                                                                  node.prev = t;
                                                                  if (compareAndSetTail(t, node)) {
                                                                       t.next = node;
                                                                  return t;
                                                             }
                                                         }
                                                    }
                                                }
                                                /**
                                                * CANCELLED = 1: 节点操作因为超时或者对应的线程被interrupt。节点不应该留在此状态,一旦达到此状态将从CHL队列中踢出。
                                                * SIGNAL = -1: 节点的继任节点是(或者将要成为)BLOCKED状态(例如通过LockSupport.park()操作),因此一个节点一旦被释放(解锁)或者取消就需要唤醒(LockSupport.unpack())它的继任节点。
                                                * CONDITION = -2:表明节点对应的线程因为不满足一个条件(Condition)而被阻塞。
                                                * 0: 正常状态,新生的非CONDITION节点都是此状态。*/
                                                private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
                                                    int ws = pred.waitStatus;
                                                    if (ws == Node.SIGNAL)
                                                        return true;
                                                    if (ws > 0) {     // 如果前一个节点的等待状态waitStatus>0,也就是前一个节点被CANCELLED了,那么就将前一个节点去掉,递归此操作直到所有前一个节点的waitStatus<=0
                                                    do {
                                                        node.prev = pred = pred.prev;
                                                    } while (pred.waitStatus > 0);
                                                        pred.next = node;
                                                    } else {     //  前一个节点等待状态waitStatus=0,修改前一个节点状态位为SINGAL,表示后面有节点等待你处理
                                                        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);     // 这个地方没有返回true,即中断,是为了执行下一次循环先尝试获取锁
                                                        return false;
                                                    }

                                                    // 3. 
                                                    static void selfInterrupt() {
                                                        Thread.currentThread().interrupt();
                                                    }

公平锁和非公平锁:UnLock

lock.unlock(); -->
                 // AQS
                 public final boolean release(int arg) {
                     if (tryRelease(arg)) {     // 1. 先尝试释放
                         Node h = head;
                         if (h != null && h.waitStatus != 0) // 这个是前面将节点状态设为signal的原因,如果不设置的话,在这个地方释放锁后不会唤醒后继节点
                             unparkSuccessor(h);     // 2. 唤醒后继节点
                             return true;
                         }
                     return false;
                 }
                                                // ReentrantLock.Sync
                                                protected final boolean tryRelease(int releases) {
                                                    int c = getState() - releases;
                                                    if (Thread.currentThread() != getExclusiveOwnerThread())     // 判断持有线程的锁是不是当前线程,若不是,则报异常
                                                        throw new IllegalMonitorStateException();
                                                    boolean free = false;
                                                    if (c == 0) {          // 若状态位为0,表示没有线程持有锁,将独自线程清空;若不等于0,可能是一个线程多次获取锁,因为ReentrantLock是可重入锁,所以不等于0的时候,只是将状态位减少,并不清空独占线程,也不唤醒下一个节点
                                                        free = true;
                                                        setExclusiveOwnerThread(null);
                                                    }
                                                    setState(c);
                                                    return free;
                                                }

                                                private void unparkSuccessor(Node node) {
                                                    int ws = node.waitStatus;
                                                    if (ws < 0)
                                                       compareAndSetWaitStatus(node, ws, 0);

                                                       Node s = node.next;     // 很关键,第一次释放锁的时候,Dump节点在这被跳过去的
                                                       if (s == null || s.waitStatus > 0) {
                                                           s = null;
                                                           for (Node t = tail; t != null && t != node; t = t.prev)     // 从tail -> head 找到最后一个有效的节点
                                                               if (t.waitStatus <= 0)
                                                                   s = t;
                                                           }
                                                           if (s != null)
                                                               LockSupport.unpark(s.thread);     // 唤醒节点,唤醒之后的节点会进入acquireQueued操作
                                                           }

举例

假设有3个线程陆续执行lock.lock():
  1.     开始状态:
               state = 0, 所有的线程都停留在lock前,这里的CPU是指正在执行lock与unlock之间的任务的线程


  1.       thread-1执行lock,由于当前没有线程正在运行,所以thread-1获取锁,直接开始运行,并将state设置1和独占线程设置为thread-1
  1.       thread-2执行lock:
               由于当前state为1且是thread-1,故而thread-2进入sync队列
               当前sync队列为空,先创建Dump节点,并将head和tail设置为Dump节点;
               创建thread-2的节点,并插入队列尾部,并将tail设置为thread-2;
               thread-2节点自旋获取锁,同时因为前驱节点的waitstatus为0,更改为-1(Signal),在自旋循环的下一次若还无法获取锁,则中断

  1.       thread-3执行lock:
               由于当前state为1且是thread-1,故而thread-3进入sync队列
               当前队列不为空,直接将创建的thread-3插入队列,并将tail设置为thread-3;
               thread-3节点自旋获取锁,同时由于前驱节点的waitstatus为0,设置为-1(Signal),在自旋循环的下一次若还无法获取锁,则中断


  1.       若此时threa-1再次执行lock, 只会将state增加1,同时之后释放锁也需要执行相应次数的unlock操作。threa-1执行unlock:
               从head.next节点开始遍历,找到第一个waitstatus <= 0的节点唤醒,在这里Dump节点感觉相当于正在运行的线程thread-1;
               此时thread-2被唤醒,并执行acquired获取锁,并将Head设置为thread-2

  1.     thread-2执行unlock:

  1.     thread-3执行unlock:
               因为thread-3节点的waitStatus为0,故而不会唤醒下一个节点


1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下 4载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
学校大创竞赛管理系统,学生上报项目内容,学院、教务处、评审专家评审。SpringBoot、SpringCloud、SpringSecurity、redis、Mysql、swagger、fastdfs、maven、vue、webpack.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值