从使用角度看 ReentrantLock 和 Condition
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的更多相关文章
- Java多线程高并发学习笔记(二)——深入理解ReentrantLock与Condition
锁的概念 从jdk发行1.5版本之后,在原来synchronize的基础上,增加了重入锁ReentrantLock. 本文就不介绍synchronize了,有兴趣的同学可以去了解一下,本文重点介绍Re ...
- Java多线程之ReentrantLock与Condition
一.ReentrantLock 1.ReentrantLock简介 ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”.ReentrantLock 类实现了 Lock ,它拥有与 sy ...
- Java多线程之wait、notify/notifyAll 详解,用wait 和notifyAll 以及synchronized实现阻塞队列,多线程拓展之ReentrantLock与Condition
前言:这几天看了很多关于多线程的知识,分享一波.(但是目前接触的项目还未用到过,最多用过线程池,想看线程池 请看我之前的博客) 关于基本的理论等 参考如下: https://www.cnblogs.c ...
- 使用 ReentrantLock 和 Condition 实现一个阻塞队列
前言 从之前的阻塞队列的源码分析中,我们知道,JDK 中的阻塞队列是使用 ReentrantLock 和 Condition 实现了,我们今天来个简易版的.代码如下: 代码 public class ...
- java 多线程 19: ReentrantLock 与 Condition
ReentrantLock ReentrantLock,一个可重入的互斥锁,它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大. Reentran ...
- Java多线程(九)之ReentrantLock与Condition
一.ReentrantLock 类 1.1 什么是reentrantlock java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 ...
- ReentrantLock 和 Condition的使用
ReentrantLock ReentrantLock可以等同于synchronized使用. ReentrantLock 类实现了Lock ,它拥有与 synchronized 相同的并发性和内存 ...
- 【转】使用者角度看bionic pthread_mutex和linux futex实现
使用者角度看bionic pthread_mutex和linux futex实现 本文所大篇幅引用的参考文章主要描述针对glibc和pthread实现:而本文的考察代码主要是android的bioni ...
- Android IOS WebRTC 音视频开发总结(四八)-- 从商业和技术的角度看视频行业的机会
本文主要从不同角度介绍视频行业的机会,文章来自博客园RTC.Blacker,支持原创,转载必须说明出处,欢迎关注个人微信公众号blacker ----------------------------- ...
随机推荐
- C#支付宝多次回调问题
//必须删除掉页面上的默认内容,不然支付宝会多次回调 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml&q ...
- 遍历一个可迭代对象中的所有元素,但是却不想使用for循环
def manual_iter(): with open('/etc/passwd') as f: try: while True: line = next(f) print(line, end='' ...
- python函数名称
一.第一类对象, 函数名的使用 函数名就是变量名, 函数名存储的是函数的内存地址 变量的命名规范: 由数字, 字母, 下划线组成 不能是数字开头, 更不能是纯数字 不能用关键字 不要太长 要有意义 不 ...
- 哈密顿绕行世界问题 HDU 2181
题意让你先输20行数表示20个城市及所相邻的三个城市(行数就是该城市),然后给你一个数,从这个(给的数就表示城市)城市出发走遍所有城市一次回到出发的城市:看着复杂,仔细想想是个不算太难的深搜题,主要你 ...
- 前端调用接口报错看不到报错响应时 console.dir
console.dir() 可以看到很多.log看不到的属性和方法
- Linux运维人员最常用 150 个命令汇总
linux 命令是对 Linux 系统进行管理的命令.对于 Linux 系统来说,无论是中央处理器.内存.磁盘驱动器.键盘.鼠标,还是用户等都是文件, Linux 系统管理的命令是它正常运行的核心,与 ...
- 如何备份MySQL数据库
在MySQL中进行数据备份的方法有两种: 1. mysqlhotcopy 这个命令会在拷贝文件之前会把表锁住,并把数据同步到数据文件中,以避免拷贝到不完整的数据文件,是最安全快捷的备份方法. 命令的使 ...
- nRF52832的SAADC
SAADC部分思维导图 1ADC原理 1.1主要特点 1)8/10/12分辨率,使用过采样可达到14位分辨率 2)多达8个通道 单端输入时使用1个通道,2个通道可组成差分输入 单端和差分输入时均可配置 ...
- MYSQL常用函数(系统信息函数)
DATABASE() 返回当前数据库名 BENCHMARK(count,expr) 将表达式expr重复运行count次 CONNECTION_ID() 返回当前客户的连接ID FOUND_ ...
- Redis持久化AOF和RDB对比
RDB持久化 AOF持久化 全量备份,一次保存整个数据库 增量备份,一次保存一个修改数据库的命令 保存的间隔较长 保存的间隔默认一秒 数据还原速度快 数据还原速度一般 save会阻塞,但bgsave或 ...