并发编程之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操作 ...
随机推荐
- dbgrideh 哪些行被选中了
在dbgrideh中允许选择多行,如何知道哪些行被选中是个BOOKMARK类型的属性.SelectedRows: TBookmarkListprocedure TForm1.Button1Click( ...
- WPF软件开发系统之五——展会展厅触摸屏企业产品宣传展示系统
本系统开发背景:上海展会多点触摸大屏(60寸以上)上互动展示. 功能包括:企业背景.产品.合作伙伴.所获荣誉等以图片.文字.视频多媒体的方式呈块状显示,亮点功能为支持多点操作去旋转.缩放.拖拽呈现各种 ...
- composer包(发布到github上)同步到Packagist
在上一篇文章里面,探讨了如何一步步建立composer包–创建你的一个composer包 创建完成后,我们需要做的就是讲自建的包发布到Packagist上.至于说什么是Packagist,这个就不用我 ...
- 老白关于rac性能调优的建议(10gRAC)
RAC应用设计方面需要在底层做很有设计.虽然ORACLE的售前人员总是说RAC的扩展性是透明的,只要把应用分到不同的节点,就可以平滑的扩展系统能力了.而事实上,RAC的CACHE FUSION机制决定 ...
- 网站升级HTTPS后WebSocket不能连接的问题
一.前端代码 var socket = new WebSocket("wss://www.smcic.cn/wss/"); 注意点: 如果网站使用HTTPS,WebSocket必须 ...
- day11(函数参数,函数对象,打散机制,函数嵌套调用)
一,复习 # 什么是函数:具体特定功能的代码块 - 特定功能代码块作为一个整体,并给该整体命名,就是函数 # 函数的优点: # 1.减少代码的冗余 # 2.结构清晰,可读性强 # 3.具有复用性,开发 ...
- iview table内渲染proptip组件
渲染proptip组件 columns: [{ title: '产品图', key: 'pic', sortable: true, render: function(h, para){ return ...
- .netcore 读取ansi编码
public class FileHelper { //根据文件自动觉察编码并输出内容 public static string GetText(string path) { StringBuilde ...
- [安全转帖]浅析安全威胁情报共享框架OpenIOC
浅析安全威胁情报共享框架OpenIOC https://www.freebuf.com/sectool/86580.html Indicator of compromise Outline: 1. I ...
- HTTP协议与TCP/IP协议
OSI 是7层 TCP/IP 协议是 4层. OIS 包括的层 从底到上依次为 1.物理层 2.数据链路层 3.网络层 4.传输层 5.会话层 6.表示层 7.应用层 TCP/IP ...