并发编程之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操作 ...
随机推荐
- There is already an object named '#xxxx' in the database.
这个案例是前几天同事遇到的一个案例,在存储过程中"删除"了一个临时表,然后重新创建这个临时表时遇到"There is already an object named 'x ...
- centos7的主机名配置
centos7的主机名配置 方法一:通过配置文件/etc/hostname (重启后生效) 方法二:通过命令hostnamectl set-hostname 新主机名(会自动把主机名改为小写) ...
- VirtualBox Network Config
Sharing Host VPN with VirtualBox guest After looking for this solution everywhere, I finally found a ...
- emacs单词首字母,单词,区域大小写转换
从光标开始,处理单词后半部分: 快捷键 说明 M-c (capitalize-word) 首字母改为大写 M-u (upcase-word) 全部改为大写 M-l (downcase-word) 全部 ...
- #033 信安培训基础题Python解决网络安全实验室|网络信息安全攻防学习平台
第三题猜猜这是经过了多少次加密?分值: 200 加密后的字符串为:一大串 字符串最后面是= 所以是base64.b64decode编码究竟为啥有=就是base64咱也不知道 咱也不敢问咋解密也是从网上 ...
- UOJ #269. 【清华集训2016】如何优雅地求和
UOJ #269. [清华集训2016]如何优雅地求和 题目链接 给定一个\(m\)次多项式\(f(x)\)的\(m+1\)个点值:\(f(0)\)到\(f(m)\). 然后求: \[ Q(f,n,x ...
- 为什么区块链和加密行业需要 Web 3?
为什么区块链和加密行业需要 Web 3? “由于人们都想从互联网上获得好处,互联网已经演变成了一个导致不公平和分裂的引擎,它被强大的力量所支配,并且任由其摆布.“——万维网之父.互联网先驱 Tim B ...
- gdb cheat sheet
0x01 控制流 r run,运行程序. r < a.txt run,重定向输入 si step instruction 进入函数 ni next instruction 下一 ...
- 转载:img是什么元素?置换元素?
转载: https://blog.csdn.net/kingliguo/article/details/52643594 img是什么元素? 应是行内元素,判断一个元素是行内元素,还是块元素,无非就是 ...
- 微信小程序-表单笔记2
本地添加4张图片并显示至页面——组件位置.设置样式.列表渲染 Q.button是一张图片,需要实现点击这张图片后选择本地图片后显示至页面,不知道怎么让本地图片将button挤到右边 S.在wxml中 ...