一 UML类图

通过类图ReentrantLock是同步锁,同一时间只能有一个线程获取到锁,其他获取该锁的线程会被阻塞而被放入AQS阻塞队列中。ReentrantLock类继承Lock接口;内部抽象类Sync实现抽象队列同步器AbstractQueuedSynchronizer;Sync类有两个子类NonfairSync(非公平锁)和FairSync(公平锁),默认构造方法使用非公平锁,可以使用带布尔参数的构造方法指定使用公平锁;ReentrantLock可以创建多个条件进行绑定。

二 原理分析

2.1 获取锁

2.1.1 void lock()方法

调用线程T调用该方法尝试获取当前锁。

①如果锁未被其他线程获取,则调用线程T成功获取到当前锁,然后设置当前锁的拥有者为调用线程T,并设置AQS的状态值state为1,然后直接返回。

②如果调用线程T之前已经获取当前锁,则只设置设置AQS的状态值state加1,然后返回。

③如果当前锁已被其他线程获取,则调用线程T放入AQS队列后阻塞挂起。

  1. public void lock() {
  2. sync.lock();//委托内部公平锁和非公平锁获取锁
  3. }
  1. //非公平锁
    final void lock() {
  2. if (compareAndSetState(0, 1))//设置AQS状态值为1
  3. setExclusiveOwnerThread(Thread.currentThread());//成功设置锁的线程拥有者
  4. else
  5. acquire(1);//失败加入到AQS队列阻塞挂起
  6. }
  7. //公平锁
  8. final void lock() {
  9. acquire(1);
  10. }

非公平锁分析:

  1. //调用父类AbstractOwnableSynchronizer方法CAS设置state值,成功返回true,失败返回false
  2. protected final boolean compareAndSetState(int expect, int update) {
  3. return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
  4. }
  5. //调用父类AbstractOwnableSynchronizer方法,设置当前线程为锁的拥有者
  6. protected final void setExclusiveOwnerThread(Thread thread) {
  7. exclusiveOwnerThread = thread;
  8. }
  1. //调用AbstractQueuedSynchronizer父类方法,第一次获取锁失败
  2. public final void acquire(int arg) {
  3. if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//排它锁类型
  4. selfInterrupt();
  5. }
  6. //NonfairSync子类,重写父类方法
  7. protected final boolean tryAcquire(int acquires) {
  8. return nonfairTryAcquire(acquires);
  9. }
  1. //Sync类非公平锁尝试获取锁
  2. final boolean nonfairTryAcquire(int acquires) {
  3. final Thread current = Thread.currentThread();
  4. int c = getState();
  5. if (c == 0) {//二次获取锁
  6. if (compareAndSetState(0, acquires)) {
  7. setExclusiveOwnerThread(current);
  8. return true;
  9. }
  10. }
  11. else if (current == getExclusiveOwnerThread()) {//当前线程已获取锁,AQS状态值加1
  12. int nextc = c + acquires;
  13. if (nextc < 0) // overflow
  14. throw new Error("Maximum lock count exceeded");
  15. setState(nextc);
  16. return true;
  17. }
  18. return false;
  19. }
  1. //AbstractQueuedSynchronizer类创建节点,添加到AQS队列后面
  2. private Node addWaiter(Node mode) {
  3. Node node = new Node(Thread.currentThread(), mode);//创建AQS队列的节点,节点类型排它锁
  4. Node pred = tail;//尾结点
  5. if (pred != null) {
  6. node.prev = pred;//新节点的前一个节点是尾结点
  7. if (compareAndSetTail(pred, node)) {//CAS机制添加节点
  8. pred.next = node;//尾结点执行新的节点
  9. return node;
  10. }
  11. }
  12. enq(node);
  13. return node;
  14. }
  1. //插入节点到队列中
    private Node enq(final Node node) {
  2. for (;;) {
  3. Node t = tail;//尾结点
  4. if (t == null) { // 尾结点为空,初始化
  5. if (compareAndSetHead(new Node()))//第一步:CAS创建头结点(哨兵节点)一个空节点
  6. tail = head;
  7. } else {
  8. node.prev = t;
  9. if (compareAndSetTail(t, node)) {//第二步:CAS设置尾结点
  10. t.next = node;
  11. return t;
  12. }
  13. }
  14. }
  15. }
  1. //
  2. final boolean acquireQueued(final Node node, int arg) {
  3. boolean failed = true;
  4. try {
  5. boolean interrupted = false;
  6. for (;;) {
  7. final Node p = node.predecessor();//新节点的前节点
  8. if (p == head && tryAcquire(arg)) {//如果p节点的前节点是头结点,并且尝试给锁枷锁
  9. setHead(node);
  10. p.next = null; // help GC
  11. failed = false;
  12. return interrupted;
  13. }
  14. if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
  15. interrupted = true;
  16. }
  17. } finally {
  18. if (failed)
  19. cancelAcquire(node);//失败解锁
  20. }
  21. }
  22. //阻塞挂起当前线程
  23. static void selfInterrupt() {
  24. Thread.currentThread().interrupt();
  25. }
  1. //
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
  2. int ws = pred.waitStatus;
  3. if (ws == Node.SIGNAL)
  4. /*
  5. * This node has already set status asking a release
  6. * to signal it, so it can safely park.
  7. */
  8. return true;
  9. if (ws > 0) {
  10. /*
  11. * Predecessor was cancelled. Skip over predecessors and
  12. * indicate retry.
  13. */
  14. do {
  15. node.prev = pred = pred.prev;
  16. } while (pred.waitStatus > 0);
  17. pred.next = node;
  18. } else {
  19. /*
  20. * waitStatus must be 0 or PROPAGATE. Indicate that we
  21. * need a signal, but don't park yet. Caller will need to
  22. * retry to make sure it cannot acquire before parking.
  23. */
  24. compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
  25. }
  26. return false;
  27. }
  28. //
  29. private final boolean parkAndCheckInterrupt() {
  30. LockSupport.park(this);
  31. return Thread.interrupted();
  32. }

公平锁分析:

  1. protected final boolean tryAcquire(int acquires) {
  2. final Thread current = Thread.currentThread();
  3. int c = getState();
  4. if (c == 0) {
  5. if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {//与非公平锁相比,区别就在标红的位置
  6. setExclusiveOwnerThread(current);
  7. return true;
  8. }
  9. }else if (current == getExclusiveOwnerThread()) {
  10. int nextc = c + acquires;
  11. if (nextc < 0)
  12. throw new Error("Maximum lock count exceeded");
  13. setState(nextc);
  14. return true;
  15. }
  16. return false;
  17. }
  1. public final boolean hasQueuedPredecessors() {
  2. // The correctness of this depends on head being initialized
  3. // before tail and on head.next being accurate if the current
  4. // thread is first in queue.
  5. Node t = tail; // Read fields in reverse initialization order
  6. Node h = head;
  7. Node s;
      //①h != t:表示AQS队列头结点和尾结点不相同,队列不为空;
      //②(s = h.next) == null || s.thread != Thread.currentThread():头结点(哨兵节点)为空或者next节点不等于当前线程
  8. return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());
  9. }

2.1.2 void lockInterruptibly()方法

与2.2.1方法相似,不同之处在于:该方法对中断进行响应,其他线程调用当前线程中断方法,抛出InterruptedException。

2.1.3 boolean tryLock()方法

尝试获取锁。注意:该方法不会引起当前线程阻塞。

2.1.4 boolean tryLock(long timeout, TimeUnit unit)方法

与2.1.3方法相似,不同之处在于:设置了超时时间。

2.2 释放锁

尝试释放锁。

如果当前线程T已获取锁,则调用该方法更新AQS状态值减1。如果当前状态值为0,则释放锁;如果当前状态值部位0,则只是减1操作。

如果当前线程T未获取锁,则调用该方法是会抛出IllegalMonitorStateException异常。

2.2.1 void unlock()方法

  1. public void unlock() {
  2. sync.release(1);
  3. }
  4. //调用AbstractQueuedSynchronizer方法
  5. public final boolean release(int arg) {
  6. if (tryRelease(arg)) {
  7. Node h = head;
  8. if (h != null && h.waitStatus != 0)
  9. unparkSuccessor(h);//唤醒线程
  10. return true;
  11. }
  12. return false;
  13. }
  14. //回调Sync类释放锁
  15. protected final boolean tryRelease(int releases) {
  16. int c = getState() - releases;
  17. if (Thread.currentThread() != getExclusiveOwnerThread())
  18. throw new IllegalMonitorStateException();
  19. boolean free = false;
  20. if (c == 0) {
  21. free = true;
  22. setExclusiveOwnerThread(null);//设置锁的拥有线程为空
  23. }
  24. setState(c);
  25. return free;
  26. }
  27. //
  28. private void unparkSuccessor(Node node) {
  29. /*
  30. * If status is negative (i.e., possibly needing signal) try
  31. * to clear in anticipation of signalling. It is OK if this
  32. * fails or if status is changed by waiting thread.
  33. */
  34. int ws = node.waitStatus;//线程阻塞等待状态
  35. if (ws < 0)
  36. compareAndSetWaitStatus(node, ws, 0);//CAS设置状态
  37.  
  38. /*
  39. * Thread to unpark is held in successor, which is normally
  40. * just the next node. But if cancelled or apparently null,
  41. * traverse backwards from tail to find the actual
  42. * non-cancelled successor.
  43. */
  44. Node s = node.next;
  45. if (s == null || s.waitStatus > 0) {
  46. s = null;
  47. for (Node t = tail; t != null && t != node; t = t.prev)//遍历AQS队列
  48. if (t.waitStatus <= 0)
  49. s = t;
  50. }
  51. if (s != null)
  52. LockSupport.unpark(s.thread);//唤醒线程
  53. }
  1. h != t

ReentrantLock原理分析的更多相关文章

  1. Java 重入锁 ReentrantLock 原理分析

    1.简介 可重入锁ReentrantLock自 JDK 1.5 被引入,功能上与synchronized关键字类似.所谓的可重入是指,线程可对同一把锁进行重复加锁,而不会被阻塞住,这样可避免死锁的产生 ...

  2. Java 线程同步组件 CountDownLatch 与 CyclicBarrier 原理分析

    1.简介 在分析完AbstractQueuedSynchronizer(以下简称 AQS)和ReentrantLock的原理后,本文将分析 java.util.concurrent 包下的两个线程同步 ...

  3. java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析

    java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...

  4. Java 线程池原理分析

    1.简介 线程池可以简单看做是一组线程的集合,通过使用线程池,我们可以方便的复用线程,避免了频繁创建和销毁线程所带来的开销.在应用上,线程池可应用在后端相关服务中.比如 Web 服务器,数据库服务器等 ...

  5. HashMap 与 ConcrrentHashMap 使用以及源码原理分析

    前奏一:HashMap面试中常见问题汇总 HashMap的工作原理是近年来常见的Java面试题,几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道HashTable和Has ...

  6. ConcurrentHashMap原理分析(1.7与1.8)-put和 get 需要执行两次Hash

    ConcurrentHashMap 与HashMap和Hashtable 最大的不同在于:put和 get 两次Hash到达指定的HashEntry,第一次hash到达Segment,第二次到达Seg ...

  7. Android视图SurfaceView的实现原理分析(示例,出错代码)

    在Android系统中,有一种特殊的视图,称为SurfaceView,它拥有独立的绘图表面,即它不与其宿主窗口共享同一个绘图表面.由于拥有独立的绘图表面,因此SurfaceView的UI就可以在一个独 ...

  8. AQS工作原理分析

      AQS工作原理分析 一.大致介绍1.前面章节讲解了一下CAS,简单讲就是cmpxchg+lock的原子操作:2.而在谈到并发操作里面,我们不得不谈到AQS,JDK的源码里面好多并发的类都是通过Sy ...

  9. Handler系列之原理分析

    上一节我们讲解了Handler的基本使用方法,也是平时大家用到的最多的使用方式.那么本节让我们来学习一下Handler的工作原理吧!!! 我们知道Android中我们只能在ui线程(主线程)更新ui信 ...

随机推荐

  1. Bash Shell之内建命令和保留字

    转载自:http://blog.chinaunix.net/uid-25880122-id-2941630.html 命令 含义 ! 保留字,逻辑非 : 不做任何事,只做参数展开 . 读取文件并在sh ...

  2. appium——如何导出夜神模拟器下载“微信”app的apk

    背景:夜神模拟器是一款功能强大的安卓模拟器,但是当我们在上面下载APP应用后,通常不知道apk文件在哪里,下面以“微信”APP为例做一下详细介绍. 一般情况下,使用夜神安卓模拟器下载的文件只能在夜神安 ...

  3. 旧版flexbox局部填坑

    本来昨晚要写一篇react的小笔记,恰好同学小聚的时候附近有个ios维修,把我的4s拿去修好,早上用我还是ios5.1系统的4s打开自己的页面,发现flexbox布局的部分是乱的,眼前一黑. what ...

  4. 终于明白了vue使用axios发送post请求时的坑及解决原理

    前言:在做项目的时候正好同事碰到了这个问题,问为什么用axios在发送请求的时候没有成功,请求不到数据,反而是报错了,下图就是报错请求本尊 vue里代码如下: this.$http.post('/ge ...

  5. DPDK Mempool 库原理(学习笔记)

    1 前置知识点学习(了解) 从CPU到实际的存储节点,依据层级划分:Channel > DIMM > Rank > Chip > Bank > Row /Column 1 ...

  6. mysql小白系列_04 binlog(未完)

    mysql打开.查看.清理binlog 1.开启日志 log_bin=/var/lib/mysql/mysql-bin mysql> show variables like '%log_bin% ...

  7. 猜想-未做 利用office组件读取excel数据

    ---未实际使用过 用SQL-Server访问Office的Access和Excel http://blog.sina.com.cn/s/blog_964237ea0101532x.html 2007 ...

  8. jQuery中prevAll得到的DOM元素顺序问题

    学习笔记,记录下学习中遇到的问题. 使用jQuery中的prevAll可以查找当前元素之前所有的同辈元素,但是却存在一个问题:得到的同辈元素的为正常顺序的反方向. 举个例子: <!doctype ...

  9. JDK安装与配置环境变量

    1.JDK的安装 (1).为什么安装jdk? JDK是java软件开发包的简称,要想开发java程序就必须安装JDK.没有JDK的话,无法编译Java程序. (2).开始安装jdk 1.官网下载jdk ...

  10. Java的集合(一)

    转载:https://blog.csdn.net/hacker_zhidian/article/details/80590428 Java集合概况就三个:List.set和map list(Array ...