前面的并发编程之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. simulink创建简单模型

    创建简单模型 您可以使用 Simulink® 对系统建模,然后仿真该系统的动态行为.Simulink 允许您创建模块图,图中的各个连接模块代表系统的各个部分,信号代表这些模块之间的输入/输出关系.Si ...

  2. MySQL 基础知识梳理学习(五)----半同步复制

    1.半同步复制的特征 (1)从库会在连接到主库时告诉主库,它是不是配置了半同步. (2)如果半同步复制在主库端是开启了的,并且至少有一个半同步复制的从节点,那么此时主库的事务线程在提交时会被阻塞并等待 ...

  3. SQL Server 增加链接服务器

    exec sp_addlinkedserver '名称' , '' , 'SQLOLEDB' , '10.102.29.xxx' exec sp_addlinkedsrvlogin '名称' , 'f ...

  4. Python的应用小案例

    1.python统计文本中每个单词出现的次数: #coding=utf-8__author__ = 'zcg' import collectionsimport os with open('abc.t ...

  5. C语言实现将日期、时间保存到文本文件中

    今天突然兴起,看来一下C语言的文件操作,以前在学习的时候,总是一带而过,觉得没有什么用处:但是现在看来,还真的没有什么用处,最后,我现在还有用到,当然这只是我的个人认为,并不能说明什么,在此我将自己写 ...

  6. java网络爬虫基础学习(四)

    jsoup的使用 jsoup介绍 jsoup是一款Java的HTML解析器,可直接解析某个URL地址.HTML文本内容.它提供了一套非常省力的API,可通过DOM,css以及类似于Jquery的操作方 ...

  7. 力扣算法题—093复原IP地址

    给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式. 示例: 输入: "25525511135" 输出: ["255.255.11.135", ...

  8. 数据库【mongodb篇】练习操作

    本文的目标是通过大量的示例,来更好的理解如果在Mongodb中进行数据操作: 初入客户端刚利用 mongod命令进入客户端环境,此时对数据库一无所知: 举目四望,想知道现在有哪些数据库,   show ...

  9. app测试中隐藏键盘

    1.参考连接 https://www.cnblogs.com/raindrop2007/articles/7849905.html 2.在项目中的使用 2.1 设置手机上的“语言输入法”,选择appi ...

  10. 系统运行缓慢,CPU 100%,以及Full GC次数过多问题的排查思路

    前言 处理过线上问题的同学基本上都会遇到系统突然运行缓慢,CPU 100%,以及Full GC次数过多的问题.当然,这些问题的最终导致的直观现象就是系统运行缓慢,并且有大量的报警. 本文主要针对系统运 ...