前面的并发编程之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()的更多相关文章

  1. 并发编程之 wait notify 方法剖析

    前言 2018 元旦快乐. 摘要: notify wait 如何使用? 为什么必须在同步块中? 使用 notify wait 实现一个简单的生产者消费者模型 底层实现原理 1. notify wait ...

  2. 并发编程之:CountDownLatch

    大家好,我是小黑,一个在互联网苟且偷生的农民工. 先问大家一个问题,在主线程中创建多个线程,在这多个线程被启动之后,主线程需要等子线程执行完之后才能接着执行自己的代码,应该怎么实现呢? Thread. ...

  3. [转载]并发编程之Operation Queue和GCD

    并发编程之Operation Queue http://www.cocoachina.com/applenews/devnews/2013/1210/7506.html 随着移动设备的更新换代,移动设 ...

  4. Java并发编程之CAS

    CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术.简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替 ...

  5. 并发编程之 Exchanger 源码分析

    前言 JUC 包中除了 CountDownLatch, CyclicBarrier, Semaphore, 还有一个重要的工具,只不过相对而言使用的不多,什么呢? Exchange -- 交换器.用于 ...

  6. 并发编程之 Condition 源码分析

    前言 Condition 是 Lock 的伴侣,至于如何使用,我们之前也写了一些文章来说,例如 使用 ReentrantLock 和 Condition 实现一个阻塞队列,并发编程之 Java 三把锁 ...

  7. python并发编程之Queue线程、进程、协程通信(五)

    单线程.多线程之间.进程之间.协程之间很多时候需要协同完成工作,这个时候它们需要进行通讯.或者说为了解耦,普遍采用Queue,生产消费模式. 系列文章 python并发编程之threading线程(一 ...

  8. python并发编程之gevent协程(四)

    协程的含义就不再提,在py2和py3的早期版本中,python协程的主流实现方法是使用gevent模块.由于协程对于操作系统是无感知的,所以其切换需要程序员自己去完成. 系列文章 python并发编程 ...

  9. python并发编程之asyncio协程(三)

    协程实现了在单线程下的并发,每个协程共享线程的几乎所有的资源,除了协程自己私有的上下文栈:协程的切换属于程序级别的切换,对于操作系统来说是无感知的,因此切换速度更快.开销更小.效率更高,在有多IO操作 ...

随机推荐

  1. java或Jmeter实现两个日期相加减(2003-06-01-2003-05-01)

    在beanshell中写入如下代码, import java.io.FileInputStream; SimpleDateFormat myFormatter = new SimpleDateForm ...

  2. 我们距离AI编程还有多远?

    近几年来,人工智能的信息以不同形式霸占着我们的眼球,我们知道AlphaGo.微软小冰.Sophia,了解过自动驾驶.无人机.智能家居等,深知人工智能是在记忆力.学习力.运算力方面都远超人类的存在,但人 ...

  3. 只需一行代码!Python中9大时间序列预测模型

    在时间序列问题上,机器学习被广泛应用于分类和预测问题.当有预测模型来预测未知变量时,在时间充当独立变量和目标因变量的情况下,时间序列预测就出现了. 预测值可以是潜在雇员的工资或银行账户持有人的信用评分 ...

  4. 如何删除Windows10操作系统资源管理器中的下载、图片、音乐、文档、视频、桌面、3D对象这7个文件夹

    通过注册表删除,步骤如下: 1.按下win+R,输入regedit,打开注册表 2.找到位置:计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Cur ...

  5. June. 26th 2018, Week 26th. Tuesday

    No affection but interests can be found in the world of animals. 在动物的世界里,只有利益,没有感情. From Animal Worl ...

  6. python一直放弃到双数的day10

    今天接着来说那个新的大知识点,函数,函数中是可以传递一个数值的,这个数值简称为参数,对于参数,他可以是任意个数和任意类型(数据类型).参数的其中一种有位置传参,函数中的参数有几个,那么就要传入几个,传 ...

  7. 好程序员技术分享html5和JavaScript的区别

    好程序员技术分享html5和JavaScript的区别,HTML5广义上讲是前端开发学科的代名词,包含HTML5.CSS3及JavaScript三个重要的部分,是运行在浏览器上应用的统称.如PC端网站 ...

  8. Oracle 11g全表扫描以Direct Path Read方式执行

    在Oracle Database 11g中有一个新特性,全表扫描可以通过直接路径读的方式来执行(Direct Path Read),这是一个合理的变化,如果全表扫描的大量数据读取是偶发性的,则直接路径 ...

  9. 【问题解决方案】查看Python安装了哪些库(pandas, matplotlib等等)

    查看方法: 一句命令:cmd打开终端后键入pip list END

  10. React生命周期详解

    React生命周期图解: 一.旧版图解: 二.新版图解: 从图中,我们可以清楚知道React的生命周期分为三个部分:  实例化.存在期和销毁时. 旧版生命周期如果要开启async rendering, ...