并发编程之wait()、notify()
前面的并发编程之volatile中我们用程序模拟了一个场景:在main方法中开启两个线程,其中一个线程t1往list里循环添加元素,另一个线程t2监听list中的size,当size等于5时,t2线程结束,t1线程继续执行,直到循环结束,上篇文章是用volatile来保证内存的可见性,从而访问共享内存来实现两个线程之间的通信,这篇文章我们用wait()和notify()来实现此功能。我们先来看看以下代码是否满足要求:
package com.fanjf.thread; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit; public class Mycontainer2 {
static List<Integer> integers = new ArrayList<>();
final static Object object = new Object(); public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
System.out.println("t1启动");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
synchronized (object) {
for(int i=0;i<10;i++){
integers.add(i);
if(integers.size()==5){
object.notify();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("add:"+i);
} }
System.out.println("t1结束"); }
}).start(); new Thread(new Runnable() {
public void run() {
System.out.println("t2启动");
synchronized (object) {
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
} }
System.out.println("t2结束"); } }).start();
} }
这段程序看着好像是没什么问题:线程t1启动,休眠1s以便让t2获得锁执行wait方法,当list的size不等于5时,t1线程执行add方法,t2线程等待,当size等于5时,t1线程通知t2线程继续执行直到t2结束再继续执行t1,下面我们看下执行结果是否跟我们预期的一样:
t1启动
t2启动
add:0
add:1
add:2
add:3
add:4
add:5
add:6
add:7
add:8
add:9
t1结束
t2结束
从结果可以看出,t2并没有立马让出线程,而是一直运行到t1结束,这里有一个非常关键的点是:notify()方法不会立即释放锁,而是要等到t1线程执行完毕才会释放锁,所以才有了上面add方法一直运行到t1结束,我们要想让t1在size等于5时让出线程给t2,光用notify()是不行的,我们应该在notify()之后紧跟着执行wait(),通过wait()来释放锁,这样t2就能执行打印结束的语句了。仔细想一想,这样就可以了吗?当t1执行wait()方法让出线程给t2之后,t2执行完毕,此时t1还是处于等待状态,执行wait()方法的线程必须要等待获得同一把锁的其他线程执行notify()方法以后此线程才能重新获得锁继续执行,这样的话此时t1线程永远都得不到执行了,所以我们应该在t2线程结束之前执行notify()让t1重新获得锁继续执行add操作,直到线程结束。改造后的代码如下:红色部分为新加部分
package com.fanjf.thread; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit; public class Mycontainer2 {
static List<Integer> integers = new ArrayList<>();
final static Object object = new Object(); public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
System.out.println("t1启动");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
synchronized (object) {
for(int i=0;i<10;i++){
integers.add(i);
if(integers.size()==5){
object.notify();
25 try {
26 object.wait();
27 } catch (InterruptedException e) {
28 e.printStackTrace();
29 }
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("add:"+i);
} }
System.out.println("t1结束"); }
}).start(); new Thread(new Runnable() {
public void run() {
System.out.println("t2启动");
synchronized (object) {
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
object.notify(); }
System.out.println("t2结束"); } }).start();
} }
运行结果如下:
t1启动
t2启动
add:0
add:1
add:2
add:3
t2结束
add:4
add:5
add:6
add:7
add:8
add:9
t1结束
此时程序运行的结果就正确了,其实,只要理解了wait()会立即释放锁,notify()不会立即释放锁,上面的代码就很容易理解了!
并发编程之wait()、notify()的更多相关文章
- 并发编程之 wait notify 方法剖析
前言 2018 元旦快乐. 摘要: notify wait 如何使用? 为什么必须在同步块中? 使用 notify wait 实现一个简单的生产者消费者模型 底层实现原理 1. notify wait ...
- 并发编程之:CountDownLatch
大家好,我是小黑,一个在互联网苟且偷生的农民工. 先问大家一个问题,在主线程中创建多个线程,在这多个线程被启动之后,主线程需要等子线程执行完之后才能接着执行自己的代码,应该怎么实现呢? Thread. ...
- [转载]并发编程之Operation Queue和GCD
并发编程之Operation Queue http://www.cocoachina.com/applenews/devnews/2013/1210/7506.html 随着移动设备的更新换代,移动设 ...
- Java并发编程之CAS
CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术.简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替 ...
- 并发编程之 Exchanger 源码分析
前言 JUC 包中除了 CountDownLatch, CyclicBarrier, Semaphore, 还有一个重要的工具,只不过相对而言使用的不多,什么呢? Exchange -- 交换器.用于 ...
- 并发编程之 Condition 源码分析
前言 Condition 是 Lock 的伴侣,至于如何使用,我们之前也写了一些文章来说,例如 使用 ReentrantLock 和 Condition 实现一个阻塞队列,并发编程之 Java 三把锁 ...
- python并发编程之Queue线程、进程、协程通信(五)
单线程.多线程之间.进程之间.协程之间很多时候需要协同完成工作,这个时候它们需要进行通讯.或者说为了解耦,普遍采用Queue,生产消费模式. 系列文章 python并发编程之threading线程(一 ...
- python并发编程之gevent协程(四)
协程的含义就不再提,在py2和py3的早期版本中,python协程的主流实现方法是使用gevent模块.由于协程对于操作系统是无感知的,所以其切换需要程序员自己去完成. 系列文章 python并发编程 ...
- python并发编程之asyncio协程(三)
协程实现了在单线程下的并发,每个协程共享线程的几乎所有的资源,除了协程自己私有的上下文栈:协程的切换属于程序级别的切换,对于操作系统来说是无感知的,因此切换速度更快.开销更小.效率更高,在有多IO操作 ...
随机推荐
- 织梦CMS增加复制文档功能
打开后台目录(/dede)下archives_do.php约430行下添加: /*----------------------------- //复制文档 ---------------------- ...
- 【工作分解法】IT人,你的工作“轻松”么?
一.前言 假如读者是一个老板,下面有两位员工,工作难度一样,完成量一样,人品和责任心也一样.一位每天加班加点,废寝忘食的工作:而另外一位每天在座位上喝着咖啡,非常的轻松自如的工作.您会更器重哪一位? ...
- 斐波那契数列(C#)
斐波那契数,亦称之为斐波那契数列(意大利语: Successione di Fibonacci),又称黄金分割数列.费波那西数列.费波拿契数.费氏数列,指的是这样一个数列:1.1.2.3.5.8.13 ...
- vue(4)—— vue的过滤器,监听属性,生命周期函数,获取DOM元素
过滤器 vue允许自定义过滤器,我个人认为,过滤器有两种,一种是对数据的清洗过滤,一种是对数据切换的动画过滤 数据切换的动画过滤 这里还是利用前面的动态组件的例子: 这里由于没办法展示动画效果,代码如 ...
- Ubuntu 16.04 安装GIMP绘图软件
Ubuntu上比较好用的绘图软件,GIMP,安装方法如下: 终端输入 : sudo apt-get install gimp ,回车,输入密码,即可安装简单易行. 输入 :gimp ,启动程序.
- eclipse中使用Lombok(转)
原文链接:https://www.cnblogs.com/justuntil/p/7120534.html windows环境 1.下载lombok.jar包https://projectlombok ...
- pyhton崩溃的第六天,又有新成员
首先在今天的刚刚开始,补充一下上次两个成员的一些特有的方法,一个是列表,一个是字典,首先列表里面多了一个反转的方法,名叫reverse,简单就是把列表中的123变成了321,还有一个方法是sort,是 ...
- 浏览器中 F12 功能的简单介绍
chrome浏览器中 F12 功能的简单介绍 由于F12是前端开发人员的利器,所以我自己也在不断摸索中,查看一些博客和资料后,自己总结了一下来帮助自己理解和记忆,也希望能帮到有需要的小伙伴,嘿嘿! 首 ...
- Linux新手随手笔记1.1
ifconfig 查询网卡信息 分别是网卡名称,物理IP地址,MAC地址,RX收到数据包大小,TX发送数据包大小 # uname # uname -a 查看内核版本号 # hostname 查看主 ...
- [OIDC in Action] 3. 基于OIDC(OpenID Connect)的SSO(添加Github OAuth 2.0的支持)
在上上一篇基于OIDC的SSO的登录页面的截图中有出现QQ登录的地方.这个其实是通过扩展OIDC的OpenID Provider来实现的,OpenID Provider简称OP,OP是OIDC的一个很 ...