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

代码示例:

public static void main(String[] args) throws InterruptedException {
final ReentrantLock lock = new ReentrantLock();
final Condition con1 = lock.newCondition();
final Condition con2 = lock.newCondition(); // lock.lock(); Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
con1.await();
System.out.println("wake from cond1");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
t1.start(); Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
con2.await();
System.out.println("wake from cond2");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
t2.start(); Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
con2.await();
System.out.println("wake from cond2");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
t3.start(); Thread.sleep(100);
try {
lock.lock();
System.out.println("lock.getQueueLength() = " + lock.getQueueLength());
System.out.println("lock.getWaitQueueLength(con1) = " + lock.getWaitQueueLength(con1));
System.out.println("lock.getWaitQueueLength(con2) = " + lock.getWaitQueueLength(con2));
con1.signal();
con2.signalAll(); Thread.sleep(100);
System.out.println("lock.getWaitQueueLength(con1) = " + lock.getWaitQueueLength(con1));
System.out.println("lock.getWaitQueueLength(con2) = " + lock.getWaitQueueLength(con2));
} finally {
lock.unlock();
} }

lock 和 await 会改变 state 的值。

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

// int java.util.concurrent.locks.ReentrantLock.getQueueLength()
public final int getQueueLength() {
return sync.getQueueLength();
} // int java.util.concurrent.locks.AbstractQueuedSynchronizer.getQueueLength()
public final int getQueueLength() {
int n = 0;
for (Node p = tail; p != null; p = p.prev) {
if (p.thread != null)
++n;
}
return n;
}

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

// int java.util.concurrent.locks.ReentrantLock.getWaitQueueLength(Condition condition)
public int getWaitQueueLength(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
} // int java.util.concurrent.locks.AbstractQueuedSynchronizer.getWaitQueueLength(ConditionObject condition)
public final int getWaitQueueLength(ConditionObject condition) {
if (!owns(condition))
throw new IllegalArgumentException("Not owner");
return condition.getWaitQueueLength();
} // int java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject.getWaitQueueLength()
protected final int getWaitQueueLength() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int n = 0;
for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
if (w.waitStatus == Node.CONDITION)
++n;
}
return n;
}

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

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

// java.util.concurrent.locks.AbstractQueuedSynchronizer.Node
static final class Node {
static final Node EXCLUSIVE = null;
// 线程阻塞节点的状态
static final int CANCELLED = 1;
static final int SIGNAL = -1;
// condition 节点的状态
static final int CONDITION = -2;
static final int PROPAGATE = -3;
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
}

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

看看 Condition 实现类的 api:

public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
private transient Node firstWaiter;
private transient Node lastWaiter; /**
* Adds a new waiter to wait queue.
* @return its new wait node
*/
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
} public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
} /**
* Moves the longest-waiting thread, if one exists, from the
* wait queue for this condition to the wait queue for the
* owning lock.
*
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
* returns {@code false}
*/
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
}

在开头的示例中,创建了 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. 安装Windows10系统注意事项

    硬盘的AHCI开启: 报错解决:将Secure Boot 设置为Disabled  win10系统下载地址:ed2k://|file|cn_windows_10_multi-edition_versi ...

  2. 前端分页插件bootstrapPaginator的使用

     引入bootstrap-paginator.js <table class="table table-striped table-bordered table-hover dataT ...

  3. JQ遇到$(‘.xxx’).attr(‘display’)一直返回undefined

    jq attr && jq css 1.1 attr() 方法设置或返回被选元素的属性值 我们就题目遇到的问题做一个测试 //html <div class="div1 ...

  4. PL/SQL Developer过期解决方法

    参考资料: plsql过期解决方法 plsql永久注册码适用个版本 方法一: 1.首先,登陆PL/SQL Developer,PL/SQL Developer要到期了 2.输入指令“regedit”打 ...

  5. JS代码排版工具

    有时候从网页上下载的js文件删除了空格,看起来就是一整段,还是蛮影响开发效率的,在CSDN上找到一个排版工具,用掉了我最后一个积分,所以在这儿分享一下: 百度云:链接:http://pan.baidu ...

  6. Gradle实现编译差异

    今天开发组长问了这么一个问题,如何实现通过gradle编译动态设置代码里的一些值.可能这么说不太明白,下面说依稀具体需求. 开发中有两个服务器:一个用于测试版本.一个用于线上版本发布,这两个服务器地址 ...

  7. 力扣(LeetCode)500. 键盘行

    给定一个单词列表,只返回可以使用在键盘同一行的字母打印出来的单词.键盘如下图所示. 示例: 输入: ["Hello", "Alaska", "Dad& ...

  8. 对pandas和pendulum的吐槽——TimeStamp numpy的datetime64的转型问题

    今天被这俩货因为时间日期处理不兼容的问题折腾半天,气死人,不吐槽不行了! 这俩简称都可以是pd的库,都TM够轴的,互相兼容极差. pandas 和 pendulum 知名度都很高,也很常用.但我就是用 ...

  9. Windows下使用创建多层文件夹 SHCreateDirectoryEx 函数需要注意的问题

    1.在使用SHCreateDirectoryEx函数创建多层文件夹的过程中,发现在文件夹路径中,只能使用\\而不能使用/,否则将创建文件夹失败. 2.下面为在MFC中使用的代码片段 CString n ...

  10. ASP.NET 4.x Web Api Odata v4 backend modify query 修改查询

    有时候我们会想给予权限添加 filter 到查询上. 比如 会员和管理员都使用了 /api/products 作为 product 查询 但是会员不应该可以看见还没有上架的货品 /api/produc ...