synchronized块中的wait()、nofity()、nofityAll()方法
前言
在Java并发编程实战,会经常遇到多个线程访问同一个资源的情况,这个时候就需要维护数据的一致性,否则会出现各种数据错误,其中一种同步方式就是利用Synchronized关键字执行锁机制,锁机制是先给共享资源上锁,只有拿到锁的线程才可以访问共享资源,其他线程进入等待状态。下面将以实例代码讲解一下
一、wait()、nofity()、nofityAll()讲解
示例代码
package thread; import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit; /**
* Created by StoneGeek on 2018/5/19.
* 博客地址:http://www.cnblogs.com/sxkgeek
* 当线程执行wait()时,会把当前的锁释放,然后让出CPU,进入等待状态。
* 当线程执行notify()/notifyAll()方法时,会唤醒一个处于等待状态该对象锁的线程,然后继续往下执行,直到执行完退出对象锁锁住的区域(synchronized修饰的代码块)后再释放锁
* 个人认为synachronized(){}执行完后会释放锁
*/
public class WaitNotify {
static boolean flag = true;
static Object lock = new Object(); public static void main(String[] args) throws Exception {
Thread waitThread = new Thread(new Wait(), "WaitThread");
waitThread.start();
TimeUnit.SECONDS.sleep();
Thread notifyThread = new Thread(new Notify(), "NotifyThread");
notifyThread.start();
} static class Wait implements Runnable {
public void run() {
// 加锁,拥有lock的Monitor
synchronized (lock) {
// 当条件不满足时,继续wait,同时释放了lock的锁
while (flag) {
System.out.println(Thread.currentThread().getName()
+ " flag is true. wait@ "
+ new SimpleDateFormat("HH:mm:ss")
.format(new Date()));
try {
lock.wait();
System.out.println("此处继续执行"+Thread.currentThread().getName());
// flag=true;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 条件满足时,完成工作
System.out.println(Thread.currentThread().getName()
+ " flag is false. running@ "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
synchronized (lock){
System.out.println(Thread.currentThread().getName()+"执行结束");
}
}
} // wait()会立刻释放synchronized(obj)中的obj锁,以便其他线程可以执行obj.notify()
// 但是notify()不会立刻立刻释放sycronized(obj)中的obj锁,必须要等notify()所在线程执行完synchronized(obj)块中的所有代码才会释放这把锁.
// yield(),sleep()不会释放锁
static class Notify implements Runnable {
public void run() {
// 加锁,拥有lock的Monitor
synchronized (lock) {
// 获取lock的锁,然后进行通知,通知时不会释放lock的锁,
// 直到当前线程释放了lock后,WaitThread才能从wait方法中返回
System.out.println(Thread.currentThread().getName()
+ " hold lock. notify @ "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
lock.notifyAll();
flag = false;
}
// 再次加锁
synchronized (lock) {
System.out.println(Thread.currentThread().getName()
+ " hold lock again. sleep@ "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
synchronized (lock){
System.out.println(Thread.currentThread().getName()+"执行结束");
}
}
}
}
执行结果如下
1 WaitThread flag is true. wait@ ::
2 NotifyThread hold lock. notify @ ::
3 NotifyThread hold lock again. sleep@ ::
4 NotifyThread执行结束
5 此处继续执行WaitThread
6 WaitThread flag is false. running@ ::
7 WaitThread执行结束
解释:
首先创建一个lock对象,然后给这个lock上锁来对多个进程同步,flag是一个标志,用来跳出while循环。
当线程执行wait()时,会把当前的锁释放,然后让出CPU,进入等待状态。
此时轮到notifythread线程,并且执行notifyAll(),这个意思是能够唤醒所有正在等待这个lock对象的monitor的线程,但是
必须要等notify()所在线程执行完synchronized(obj)块中的所有代码才会释放这把锁,
此时接着waitthread被唤醒,继续执行while循环,执行完之后,由于flag在notifythread中置为false,所以跳出while循环(如果在实例代码的wail()后加flag=true结果是截然不同,由于notirythread进程执行完,此时会一直陷入wait,大家可以试试),
执行console打印5 6 7
notify()与notifyAll()的区别
notify()方法能够唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象
的monitor的话,则只能唤醒其中一个线程
而调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程
当时的疑惑
(1)既然notify或者notifyAll需要执行完synchronized块中的内容,那么他还有什么存在的价值的
后来执行完之后,才发现要是没有这个方法,那么synchronized块执行完之后,waitthread还是在等待状态,无法被唤醒。
(2)wait被notify唤醒之后,是接着执行,所以console打印5,并不是从头执行(如果在实例代码的wail()后加flag=true结果是截然不同,由于notirythread进程执行完,waitthread进程重新执行wait方法,此时会一直陷入wait,无其他进程唤醒此进程)
二、wait()/wait(long)和sleep(long)方法的区别
将示例代码中的lock.wait()改为Thread.sleep(1000),console打印
WaitThread flag is true. wait@ ::
此处继续执行WaitThread
WaitThread flag is true. wait@ ::
此处继续执行WaitThread
WaitThread flag is true. wait@ ::
此处继续执行WaitThread
WaitThread flag is true. wait@ ::
此处继续执行WaitThread
由此说明sleep并没有释放锁。
区别:
1、Sleep(long)是Thread的方法,而wait()/wait(long)是Object的方法
2、Sleep(long)可以放在sychnoized块内也可以不放在里面,但是wait()/wait(long)必须放在语句块内
3、Sleep(long)不释放锁,只是让当前线程暂停一段时间,而wait()/wait(long)是释放锁
4、wait()将当前线程放到阻塞队列,只有调用notify()/notifyAll()方法后,才将其从阻塞队列中移动到就绪队列,等待被CPU调度,而wait(long)方法执行后就是放到阻塞队列,等待时间到期或者被wait()/wait(long)唤醒后就可以放到就绪队列被CPU调度
目前还有一个疑惑,就是线程中的run方法有两个同样的synchroized(lock),是不是跟一个synchroized(lock)效果是一样的,目前就运行结果来看是这样子的!
synchronized块中的wait()、nofity()、nofityAll()方法的更多相关文章
- 九、dbms_ddl(提供了在PL/SQL块中执行DDL语句的方法)
1.概述 作用:提供了在PL/SQL块中执行DDL语句的方法,并且也提供了一些DDL的特殊管理方法. 2.包的组成 1).alter_compile说明:用于重新编译过程.函数和包语法:dbms_dd ...
- 调用notify()后,当前线程执行完synchronized块中的所有代码才会释放锁
package com.pinnet.test; public class Demo { public static void main(String[] args) { Demo demo = ne ...
- java 为什么wait(),notify(),notifyAll()必须在同步(Synchronized)方法/代码块中调用?
wait()作用:该方法用来将当前线程置入休眠状态,直到接到通知或被中断为止.条件:在调用wait()之前,线程必须要获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法.进入wai ...
- Java多线程初学者指南(11):使用Synchronized块同步方法
synchronized关键字有两种用法.第一种就是在<使用Synchronized关键字同步类方法>一文中所介绍的直接用在方法的定义中.另外一种就是synchronized块.我们不仅可 ...
- 使用Synchronized块同步方法
synchronized关键字有两种用法.第一种就是在<使用Synchronized关键字同步类方法>一文中所介绍的直接用在方法的定义中.另外一种就是synchronized块.我们不仅可 ...
- 为什么WAIT必须在同步块中
我们知道java的Object有wait和notify方法,如果要使用wait和notify的话,那么必须在synchronized块中,否则会抛出IllegalMonitorStateExcepti ...
- -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中
本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait( ...
- Java中wait()方法为什么要放在同步块中?(lost wake-up 问题)
问题起源 事情得从一个多线程编程里面臭名昭著的问题"Lost wake-up problem"说起. 这个问题并不是说只在Java语言中会出现,而是会在所有的多线程环境下出现. 假 ...
- 阿里面试题,为什么wait()方法要放在同步块中?
某天我在***的时候,突然有个小伙伴微信上说:“哥,阿里面试又又挂了,被问到为什么wait()方法要放在同步块中,没答出来!” 我顿时觉得**一紧,仔细回顾一下,如果wait()方法不在同步块中,代码 ...
随机推荐
- 基于单细胞测序数据构建细胞状态转换轨迹(cell trajectory)方法总结
细胞状态转换轨迹构建示意图(Trapnell et al. Nature Biotechnology, 2014) 在各种生物系统中,细胞都会展现出一系列的不同状态(如基因表达的动态变化等),这些状态 ...
- 记一次tomcat内存大涨到溢出的经历
前一段时间提交了一个产品版本给测试人员测试,测试结果简直出人意料! 测试一段时间后页面就卡死了,当时根据这个现象下意识的怀疑是卡到数据库这一层,然后查看数据库连接相关的参数,如意料之中的相似,连接数太 ...
- Python学习之turtle库和蟒蛇绘制程序
Python的函数库 Python语言与C语言Java类似,可以大量使用外部函数库包含在安装包中的函数库:. 比如math, random, turtle等其他函数库,其他函数库用户根据代码需求自行安 ...
- MySQL Explain学习笔记
目录 一.执行计划概念 二.Explain用法 三.Explain属性介绍 3.1 id属性 3.2 select_type属性 3.3 table属性 3.4 type属性 3.5 possible ...
- SpringCloud(三)Ribbon与Feign
上一篇使用了Eureka与Ribbon组件做了最简单的的服务注册与发现,我们知道Eureka是实现服务治理中心的组件,但是上一篇Eureka没有实现集群,这样没有保证到Eureka Server的高可 ...
- 《Java 8 in Action》Chapter 10:用Optional取代null
1965年,英国一位名为Tony Hoare的计算机科学家在设计ALGOL W语言时提出了null引用的想法.ALGOL W是第一批在堆上分配记录的类型语言之一.Hoare选择null引用这种方式,& ...
- Android系统修改之Email自动回复功能分析
1. Email添加自动回复功能需要注意事项 Email可能存在多个账户, 因此自动回复功能应该添加在账户设置里面, 自动回复针对一个账户单独处理 在Email账户设置里面, 开启自动回复功能的时, ...
- 03 (H5*) Vue第三天
目录: 1:Vue-resource中的全局配置. 2:Vue动画2部曲 3:animate动画 4:钩子函数动画 5:组件三部曲,推荐使用template标签来创建组件模板 1:Vue-resour ...
- Spark学习之Scala的基础知识
Scala的变量声明 在Scala创建变量的时候,必须使用val或者var val,变量值不可修改,一旦分配不能重新指向别的值 var,分配后,可重新指向类型相同的值 举例 val lines = s ...
- 29道Zookeeper面试题超详细(附答案)
原文链接 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一致性服务的软件 ...