java 语言中谈到锁,少不了比较一番 synchronized 和 ReentrantLock 的原理,本文不作分析,只是简单介绍一下 ReentrantLock 的用法,从使用中推测其内部的一些原理。

代码示例:

  1. public static void main(String[] args) throws InterruptedException {
  2. final ReentrantLock lock = new ReentrantLock();
  3. final Condition con1 = lock.newCondition();
  4. final Condition con2 = lock.newCondition();
  5.  
  6. // lock.lock();
  7.  
  8. Thread t1 = new Thread(new Runnable() {
  9. @Override
  10. public void run() {
  11. try {
  12. lock.lock();
  13. con1.await();
  14. System.out.println("wake from cond1");
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. } finally {
  18. lock.unlock();
  19. }
  20. }
  21. });
  22. t1.start();
  23.  
  24. Thread t2 = new Thread(new Runnable() {
  25. @Override
  26. public void run() {
  27. try {
  28. lock.lock();
  29. con2.await();
  30. System.out.println("wake from cond2");
  31. } catch (InterruptedException e) {
  32. e.printStackTrace();
  33. } finally {
  34. lock.unlock();
  35. }
  36. }
  37. });
  38. t2.start();
  39.  
  40. Thread t3 = new Thread(new Runnable() {
  41. @Override
  42. public void run() {
  43. try {
  44. lock.lock();
  45. con2.await();
  46. System.out.println("wake from cond2");
  47. } catch (InterruptedException e) {
  48. e.printStackTrace();
  49. } finally {
  50. lock.unlock();
  51. }
  52. }
  53. });
  54. t3.start();
  55.  
  56. Thread.sleep(100);
  57. try {
  58. lock.lock();
  59. System.out.println("lock.getQueueLength() = " + lock.getQueueLength());
  60. System.out.println("lock.getWaitQueueLength(con1) = " + lock.getWaitQueueLength(con1));
  61. System.out.println("lock.getWaitQueueLength(con2) = " + lock.getWaitQueueLength(con2));
  62. con1.signal();
  63. con2.signalAll();
  64.  
  65. Thread.sleep(100);
  66. System.out.println("lock.getWaitQueueLength(con1) = " + lock.getWaitQueueLength(con1));
  67. System.out.println("lock.getWaitQueueLength(con2) = " + lock.getWaitQueueLength(con2));
  68. } finally {
  69. lock.unlock();
  70. }
  71.  
  72. }

lock 和 await 会改变 state 的值。

以 ReentrantLock.getQueueLength 和 ReentrantLock.getWaitQueueLength 作为入口:前者作用是返回获取锁失败,处于等待状态的线程个数,后者是返回等待某一个 condition 的线程个数。

  1. // int java.util.concurrent.locks.ReentrantLock.getQueueLength()
  2. public final int getQueueLength() {
  3. return sync.getQueueLength();
  4. }
  5.  
  6. // int java.util.concurrent.locks.AbstractQueuedSynchronizer.getQueueLength()
  7. public final int getQueueLength() {
  8. int n = 0;
  9. for (Node p = tail; p != null; p = p.prev) {
  10. if (p.thread != null)
  11. ++n;
  12. }
  13. return n;
  14. }

很简单,就是遍历阻塞线程的链表。

  1. // int java.util.concurrent.locks.ReentrantLock.getWaitQueueLength(Condition condition)
  2. public int getWaitQueueLength(Condition condition) {
  3. if (condition == null)
  4. throw new NullPointerException();
  5. if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
  6. throw new IllegalArgumentException("not owner");
  7. return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
  8. }
  9.  
  10. // int java.util.concurrent.locks.AbstractQueuedSynchronizer.getWaitQueueLength(ConditionObject condition)
  11. public final int getWaitQueueLength(ConditionObject condition) {
  12. if (!owns(condition))
  13. throw new IllegalArgumentException("Not owner");
  14. return condition.getWaitQueueLength();
  15. }
  16.  
  17. // int java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject.getWaitQueueLength()
  18. protected final int getWaitQueueLength() {
  19. if (!isHeldExclusively())
  20. throw new IllegalMonitorStateException();
  21. int n = 0;
  22. for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
  23. if (w.waitStatus == Node.CONDITION)
  24. ++n;
  25. }
  26. return n;
  27. }

同样,也是遍历链表,不同的是,这是 Condition 的链表。

现在,我们知道,ReentrantLock 中有 2 种不同的链表,其一是阻塞线程,其二是 Condition 等待链表,2 种链表都是使用 Node:

  1. // java.util.concurrent.locks.AbstractQueuedSynchronizer.Node
  2. static final class Node {
  3. static final Node EXCLUSIVE = null;
  4. // 线程阻塞节点的状态
  5. static final int CANCELLED = 1;
  6. static final int SIGNAL = -1;
  7. // condition 节点的状态
  8. static final int CONDITION = -2;
  9. static final int PROPAGATE = -3;
  10. volatile int waitStatus;
  11. volatile Node prev;
  12. volatile Node next;
  13. volatile Thread thread;
  14. }

节点的类型表明链表的类型。

看看 Condition 实现类的 api:

  1. public class ConditionObject implements Condition, java.io.Serializable {
  2. private static final long serialVersionUID = 1173984872572414699L;
  3. private transient Node firstWaiter;
  4. private transient Node lastWaiter;
  5.  
  6. /**
  7. * Adds a new waiter to wait queue.
  8. * @return its new wait node
  9. */
  10. private Node addConditionWaiter() {
  11. Node t = lastWaiter;
  12. // If lastWaiter is cancelled, clean out.
  13. if (t != null && t.waitStatus != Node.CONDITION) {
  14. unlinkCancelledWaiters();
  15. t = lastWaiter;
  16. }
  17. Node node = new Node(Thread.currentThread(), Node.CONDITION);
  18. if (t == null)
  19. firstWaiter = node;
  20. else
  21. t.nextWaiter = node;
  22. lastWaiter = node;
  23. return node;
  24. }
  25.  
  26. public final void await() throws InterruptedException {
  27. if (Thread.interrupted())
  28. throw new InterruptedException();
  29. Node node = addConditionWaiter();
  30. int savedState = fullyRelease(node);
  31. int interruptMode = 0;
  32. while (!isOnSyncQueue(node)) {
  33. LockSupport.park(this);
  34. if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
  35. break;
  36. }
  37. if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
  38. interruptMode = REINTERRUPT;
  39. if (node.nextWaiter != null) // clean up if cancelled
  40. unlinkCancelledWaiters();
  41. if (interruptMode != 0)
  42. reportInterruptAfterWait(interruptMode);
  43. }
  44.  
  45. /**
  46. * Moves the longest-waiting thread, if one exists, from the
  47. * wait queue for this condition to the wait queue for the
  48. * owning lock.
  49. *
  50. * @throws IllegalMonitorStateException if {@link #isHeldExclusively}
  51. * returns {@code false}
  52. */
  53. public final void signal() {
  54. if (!isHeldExclusively())
  55. throw new IllegalMonitorStateException();
  56. Node first = firstWaiter;
  57. if (first != null)
  58. doSignal(first);
  59. }
  60. }

在开头的示例中,创建了 2 个 Condition 对象,每个Condition 对象有一个自己的等待链表。

从使用角度看 ReentrantLock 和 Condition的更多相关文章

  1. Java多线程高并发学习笔记(二)——深入理解ReentrantLock与Condition

    锁的概念 从jdk发行1.5版本之后,在原来synchronize的基础上,增加了重入锁ReentrantLock. 本文就不介绍synchronize了,有兴趣的同学可以去了解一下,本文重点介绍Re ...

  2. Java多线程之ReentrantLock与Condition

    一.ReentrantLock 1.ReentrantLock简介 ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”.ReentrantLock 类实现了 Lock ,它拥有与 sy ...

  3. Java多线程之wait、notify/notifyAll 详解,用wait 和notifyAll 以及synchronized实现阻塞队列,多线程拓展之ReentrantLock与Condition

    前言:这几天看了很多关于多线程的知识,分享一波.(但是目前接触的项目还未用到过,最多用过线程池,想看线程池 请看我之前的博客) 关于基本的理论等 参考如下: https://www.cnblogs.c ...

  4. 使用 ReentrantLock 和 Condition 实现一个阻塞队列

    前言 从之前的阻塞队列的源码分析中,我们知道,JDK 中的阻塞队列是使用 ReentrantLock 和 Condition 实现了,我们今天来个简易版的.代码如下: 代码 public class ...

  5. java 多线程 19: ReentrantLock 与 Condition

    ReentrantLock ReentrantLock,一个可重入的互斥锁,它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大. Reentran ...

  6. Java多线程(九)之ReentrantLock与Condition

    一.ReentrantLock 类   1.1 什么是reentrantlock   java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 ...

  7. ReentrantLock 和 Condition的使用

    ReentrantLock  ReentrantLock可以等同于synchronized使用. ReentrantLock 类实现了Lock ,它拥有与 synchronized 相同的并发性和内存 ...

  8. 【转】使用者角度看bionic pthread_mutex和linux futex实现

    使用者角度看bionic pthread_mutex和linux futex实现 本文所大篇幅引用的参考文章主要描述针对glibc和pthread实现:而本文的考察代码主要是android的bioni ...

  9. Android IOS WebRTC 音视频开发总结(四八)-- 从商业和技术的角度看视频行业的机会

    本文主要从不同角度介绍视频行业的机会,文章来自博客园RTC.Blacker,支持原创,转载必须说明出处,欢迎关注个人微信公众号blacker ----------------------------- ...

随机推荐

  1. 3.2 git命令大全

    1. 常用命令 -- 查看 git remote:要查看当前配置有哪些远程仓库; git remote -v: -v 参数,你还可以看到每个别名的实际链接地址; git branch -a :查看远程 ...

  2. <script src="../build/browser.min.js"></script> 是用来里面babel工具把ES6的语法转成ES5

    <!DOCTYPE html> <html> <head> <script src="../build/react.js">< ...

  3. MySQL的正则表达式的LIKE和REGEXP区别

    LIKE匹配整个列.如果被匹配的文本在列值 中出现,LIKE将不会找到它,相应的行也不被返回(除非使用 通配符).而REGEXP在列值内进行匹配,如果被匹配的文本在 列值中出现,REGEXP将会找到它 ...

  4. C++的虚函数

    1 多态产生的背景  希望同一个方法在派生类和基类中的行为是不同的,换句话来说,方法的行为取决于调用该方法的对象. 2 解决多态的两种方法  1)在派生类中重新定义基类的方法  2)使用虚方法 3 虚 ...

  5. Python全栈开发-Day5-常用模块学习

    本节大纲: 模块介绍 time &datetime模块 random os sys shutil shelve xml处理 pyyaml处理 configparser hashlib re正则 ...

  6. 第 8 章 容器网络 - 053 - overlay 是如何隔离的?

    overlay 是如何隔离的? 不同的 overlay 网络是相互隔离的. 创建第二个 overlay 网络 ov_net2 并运行容器 bbox3. docker network create -d ...

  7. (转)基于C#的socket编程的TCP异步实现

    一.摘要 本篇博文阐述基于TCP通信协议的异步实现. 二.实验平台 Visual Studio 2010 三.异步通信实现原理及常用方法 3.1 建立连接 在同步模式中,在服务器上使用Accept方法 ...

  8. java8新特性(二)_lambda表达式

    最近一直找java8相关新特性的文章,发现都太没有一个连贯性,毕竟大家写博客肯定都有自己的侧重点,这里找到一本书,专门介绍java8新特性的,感觉大家可以看看<写给大忙人看的JavaSE8> ...

  9. spring ----> 搭建spring+springmvc+mybatis出现的几个问题

    环境: idea ce 2018.1+maven3.5.3+mysql8.0.11+jdk1.8 spring4.3.7+spring mvc4.3.7+mybatis3.4.1+tomcat7.0. ...

  10. LeetCode--012--整数转罗马数字(java)

    罗马数字包含以下七种字符: I, V, X, L,C,D 和 M. 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如, 罗马数字 2 写做 II ,即为两个并 ...