java 多线程 19: ReentrantLock 与 Condition
ReentrantLock
ReentrantLock,一个可重入的互斥锁,它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
ReentrantLock基本用法
先来看一下ReentrantLock的基本用法:
public class ThreadDomain38
{
private Lock lock = new ReentrantLock(); public void testMethod()
{
try
{
lock.lock();
for (int i = 0; i < 2; i++)
{
System.out.println("ThreadName = " + Thread.currentThread().getName() +
", i = " + i);
}
}
finally
{
lock.unlock();
}
}
}
public class MyThread38 extends Thread
{
private ThreadDomain38 td; public MyThread38(ThreadDomain38 td)
{
this.td = td;
} public void run()
{
td.testMethod();
}
}
public static void main(String[] args)
{
ThreadDomain38 td = new ThreadDomain38();
MyThread38 mt0 = new MyThread38(td);
MyThread38 mt1 = new MyThread38(td);
MyThread38 mt2 = new MyThread38(td);
mt0.start();
mt1.start();
mt2.start();
}
看一下运行结果:
ThreadName = Thread-1, i = 0
ThreadName = Thread-1, i = 1
ThreadName = Thread-0, i = 0
ThreadName = Thread-0, i = 1
ThreadName = Thread-2, i = 0
ThreadName = Thread-2, i = 1
没有任何的交替,数据都是分组打印的,说明了一个线程打印完毕之后下一个线程才可以获得锁去打印数据,这也证明了ReentrantLock具有加锁的功能
ReentrantLock持有的对象监视器非当前类的对象
前面已经证明了ReentrantLock具有加锁功能,但我们还不知道ReentrantLock持有的是什么锁,因此写个例子看一下:
public class ThreadDomain39
{
private Lock lock = new ReentrantLock(); public void methodA()
{
try
{
lock.lock();
System.out.println("MethodA begin ThreadName = " + Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("MethodA end ThreadName = " + Thread.currentThread().getName());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
lock.unlock();
} } public void methodB()
{
lock.lock();
System.out.println("MethodB begin ThreadName = " + Thread.currentThread().getName());
System.out.println("MethodB begin ThreadName = " + Thread.currentThread().getName());
lock.unlock();
}
}
写两个线程分别调用methodA()和methodB()方法:
public class MyThread39_0 extends Thread
{
private ThreadDomain39 td; public MyThread39_0(ThreadDomain39 td)
{
this.td = td;
} public void run()
{
td.methodA();
}
}
public class MyThread39_1 extends Thread
{
private ThreadDomain39 td; public MyThread39_1(ThreadDomain39 td)
{
this.td = td;
} public void run()
{
td.methodB();
}
}
写一个main函数启动这两个线程:
public static void main(String[] args)
{
ThreadDomain39 td = new ThreadDomain39();
MyThread39_0 mt0 = new MyThread39_0(td);
MyThread39_1 mt1 = new MyThread39_1(td);
mt0.start();
mt1.start();
}
看一下运行结果:
MethodB begin ThreadName = Thread-1
MethodB begin ThreadName = Thread-1
MethodA begin ThreadName = Thread-0
MethodA end ThreadName = Thread-0
看不见时间,不过第四确实是格了5秒左右才打印出来的。从结果来看,已经证明了ReentrantLock持有的是对象监视器,可以写一段代码进一步证明这一结论,即去掉methodB()内部和锁相关的代码,只留下两句打印语句:
MethodA begin ThreadName = Thread-0
MethodB begin ThreadName = Thread-1
MethodB begin ThreadName = Thread-1
MethodA end ThreadName = Thread-0
看到交替打印了,进一步证明了ReentrantLock持有的是"对象监视器"的结论。
不过注意一点,ReentrantLock虽然持有对象监视器,但是和synchronized持有的对象监视器不是一个意思,虽然我也不清楚两个持有的对象监视器有什么区别,不过把methodB()方法用synchronized修饰,methodA()不变,两个方法还是异步运行的,所以就记一个结论吧----ReentrantLock和synchronized持有的对象监视器不同,可以理解为ReentrantLock锁的就是lock这个对象,而synchronized锁的是当前这个类。的对象
另外,千万别忘了,ReentrantLock持有的锁是需要手动去unlock()的
Condition
synchronized与wait()和nitofy()/notifyAll()方法相结合可以实现等待/通知模型,ReentrantLock同样可以,但是需要借助Condition,且Condition有更好的灵活性,具体体现在:
1、一个Lock里面可以创建多个Condition实例,实现多路通知
2、notify()方法进行通知时,被通知的线程时Java虚拟机随机选择的,但是ReentrantLock结合Condition可以实现有选择性地通知,这是非常重要的
看一下利用Condition实现等待/通知模型的最简单用法,下面的代码注意一下,await()和signal()之前,必须要先lock()获得锁,使用完毕在finally中unlock()释放锁,这和wait()/notify()/notifyAll()使用前必须先获得对象锁是一样的:
public class ThreadDomain40
{
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition(); public void await()
{
try
{
lock.lock();
System.out.println("await时间为:" + System.currentTimeMillis());
condition.await();
System.out.println("await等待结束");
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
lock.unlock();
}
} public void signal()
{
try
{
lock.lock();
System.out.println("signal时间为:" + System.currentTimeMillis());
condition.signal();
}
finally
{
lock.unlock();
}
}
}
public class MyThread40 extends Thread
{
private ThreadDomain40 td; public MyThread40(ThreadDomain40 td)
{
this.td = td;
} public void run()
{
td.await();
}
}
public static void main(String[] args) throws Exception
{
ThreadDomain40 td = new ThreadDomain40();
MyThread40 mt = new MyThread40(td);
mt.start();
Thread.sleep(3000);
td.signal();
}
看一下运行结果:
await时间为:1443970329524
signal时间为:1443970332524
await等待结束
差值是3000毫秒也就是3秒,符合代码预期,成功利用ReentrantLock的Condition实现了等待/通知模型。其实这个例子还证明了一点,Condition的await()方法是释放锁的,原因也很简单,要是await()方法不释放锁,那么signal()方法又怎么能调用到Condition的signal()方法呢?
注意要是用一个Condition的话,那么多个线程被该Condition给await()后,调用Condition的signalAll()方法唤醒的是所有的线程。如果想单独唤醒部分线程该怎么办呢?new出多个Condition就可以了,这样也有助于提升程序运行的效率。使用多个Condition的场景是很常见的,像ArrayBlockingQueue里就有。
Condition可以理解为,当Condition声明多个的时候,一个lock里面有分支来判断唤醒哪个Condition,但是多个Condition只是共享一个锁的监视器的,这个wait一样,不同的是wait只支持一个对象就是当前锁对象,而Condition支持多个就是当前Condition这个对象和lock绑在一起。
注意这里如果是用Condition 实现生产消费模型,同理也是要使用signalAll()方法进行全部唤醒,否则也会出现线程假死情况,还可以分两个Condition ,一个代表生产者,一个代表消费者,唤醒停止对应的Condition 就可以不用signalAll
java 多线程 19: ReentrantLock 与 Condition的更多相关文章
- java多线程(7)---Condition
Condition 一.Condition概述 在线程的同步时可以使一个线程阻塞而等待一个信号,同时放弃锁使其他线程可以能竞争到锁. 在synchronized中我们可以使用Object的wait() ...
- Java多线程19:定时器Timer
前言 定时/计划功能在Java应用的各个领域都使用得非常多,比方说Web层面,可能一个项目要定时采集话单.定时更新某些缓存.定时清理一批不活跃用户等等.定时计划任务功能在Java中主要使用的就是Tim ...
- Java多线程高并发学习笔记(二)——深入理解ReentrantLock与Condition
锁的概念 从jdk发行1.5版本之后,在原来synchronize的基础上,增加了重入锁ReentrantLock. 本文就不介绍synchronize了,有兴趣的同学可以去了解一下,本文重点介绍Re ...
- Java多线程之wait、notify/notifyAll 详解,用wait 和notifyAll 以及synchronized实现阻塞队列,多线程拓展之ReentrantLock与Condition
前言:这几天看了很多关于多线程的知识,分享一波.(但是目前接触的项目还未用到过,最多用过线程池,想看线程池 请看我之前的博客) 关于基本的理论等 参考如下: https://www.cnblogs.c ...
- Java多线程(九)之ReentrantLock与Condition
一.ReentrantLock 类 1.1 什么是reentrantlock java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 ...
- Java多线程11:ReentrantLock的使用和Condition
ReentrantLock ReentrantLock,一个可重入的互斥锁,它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大. Reentran ...
- Java并发控制:ReentrantLock Condition使用详解
生产者-消费者(producer-consumer)问题,也称作有界缓冲区(bounded-buffer)问题,两个进程共享一个公共的固定大小的缓冲区.其中一个是生产者,用于将消息放入缓冲区:另外一个 ...
- Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock
本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...
- Java多线程系列--“JUC锁”06之 Condition条件
概要 前面对JUC包中的锁的原理进行了介绍,本章会JUC中对与锁经常配合使用的Condition进行介绍,内容包括:Condition介绍Condition函数列表Condition示例转载请注明出处 ...
随机推荐
- iOS 10 的一个重要更新-线程竞态检测工具 Thread Sanitizer
本文介绍了 Xcode 8 的新出的多线程调试工具 Thread Sanitizer,可以在 app 运行时发现线程竞态. 想想一下,你的 app 已经近乎大功告成:它经过精良的打磨,单元测试全覆盖. ...
- Apache Rewrite规则详解[转]
1.Rewrite规则简介:Rewirte主要的功能就是实现URL的跳转,它的正则表达式是基于Perl语言.可基于服务器级的(httpd.conf)和目录级的(.htaccess)两种方式.如果要想用 ...
- ss is one another utility to investigate sockets(特适合大规模tcp链接)
原创文章,转载请注明: 转载自系统技术非业余研究 本文链接地址: ss is one another utility to investigate sockets(特适合大规模tcp链接) 具体的可以 ...
- 树莓派进阶之路 (005) - 树莓派Zsh安装脚本(原创)
zsh.sh #!/bin/bash cd #安装zsh sudo apt-get install zsh #查看zsh cat /etc/shells #更改zsh chsh -s /bin/zsh ...
- WCF Service中HttpContext.Current为null的解决办法
1. 在hosting WCF的web.config中加入: <system.serviceModel> <serviceHostingEnvironment aspNetCompa ...
- 【Redis】Redis的常规操作命令
NoSQL是一种非关系型数据库,非关系型数据库库和传统的关系型数据库不同,非关系性体现在不需要依赖表进行数据存储.常见的非关系型数据库有Redis.MonoDB.HBase等,这些是基于key-val ...
- opencv 中出现错误 -215:Assertion failed
cv2.error: OpenCV(4.0.1) D:\Build\OpenCV\opencv-4.0.1\modules\imgproc\src\color.cpp:181: error: (-21 ...
- 最优化方法:共轭梯度法(Conjugate Gradient)
http://blog.csdn.net/pipisorry/article/details/39891197 共轭梯度法(Conjugate Gradient) 共轭梯度法(英语:Conjugate ...
- J2EEweb开发中的缓存问题的研究
一般情况下,浏览器都会缓存已经访问过的页面内容,关于如何禁止浏览器缓存的介绍,在网上到处都有相关的文章,但是,关于浏览器如何利用缓存,如何处理缓存的讲解,却鲜有人谈及 .浏览器在访问已缓存过的资源时, ...
- 【转】java原理—反射机制
一.什么是反射:反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.这一概念的提出很快引发了计算机科学领域关于应用反射性的研究.它首先被程序语言 ...