探索JAVA并发 - 终于搞懂了sleep/wait/notify/notifyAll
> sleep/wait/notify/notifyAll分别有什么作用?它们的区别是什么?wait时为什么要放在循环里而不能直接用if?
## 简介
首先对几个相关的方法做个简单解释,Object中有几个用于线程同步的方法:wait、notify、notifyAll。
```java
public class Object {
public final native void wait(long timeout) throws InterruptedException;
public final native void notify();
public final native void notifyAll();
}
```
+ wait: 释放当前锁,阻塞直到被notify或notifyAll唤醒,或者超时,或者线程被中断(InterruptedException)
+ notify: 任意选择一个(无法控制选哪个)正在这个对象上等待的线程把它唤醒,其它线程依然在等待被唤醒
+ notifyAll: 唤醒所有线程,让它们去竞争,不过也只有一个能抢到锁
+ sleep: 不是Object中的方法,而是Thread类的静态方法,让当前线程持有锁阻塞指定时间
## sleep和wait
sleep和wait都可以让线程阻塞,也都可以指定超时时间,甚至还都会抛出中断异常InterruptedException。
而它们最大的区别就在于,sleep时线程依然持有锁,别人无法进当前同步方法;wait时放弃了持有的锁,其它线程有机会进入该同步方法。多次提到同步方法,因为wait必须在synchronized同步代码块中,否则会抛出异常IllegalMonitorStateException,notify也是如此,可以说wait和notify是就是为了在同步代码中做线程调度而生的。
下面一个简单的例子展现sleep和wait的区别:
```java
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
// 日志行号记录
private AtomicInteger count = new AtomicInteger();
public static void main(String[] args) throws InterruptedException {
Main main = new Main();
// 开启两个线程去执行test方法
new Thread(main::test).start();
new Thread(main::test).start();
}
private synchronized void test() {
try {
log("进入了同步方法,并开始睡觉,1s");
// sleep不会释放锁,因此其他线程不能进入这个方法
Thread.sleep(1000);
log("睡好了,但没事做,有事叫我,等待2s");
//阻塞在此,并且释放锁,其它线程可以进入这个方法
//当其它线程调用此对象的notify或者notifyAll时才有机会停止阻塞
//就算没有人notify,如果超时了也会停止阻塞
wait(2000);
log("我要走了,但我要再睡一觉,10s");
//这里睡的时间很长,因为没有释放锁,其它线程就算wait超时了也无法继续执行
Thread.sleep(10000);
log("走了");
notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 打印日志
private void log(String s) {
System.out.println(count.incrementAndGet() + " "
+ new Date().toString().split(" ")[3]
+ "\t" + Thread.currentThread().getName() + " " + s);
}
}
/* 输出:
1 00:13:23 Thread-0 进入了同步方法,并开始睡觉,1s
2 00:13:24 Thread-0 睡好了,但没事做,有事叫我,等待2s
3 00:13:24 Thread-1 进入了同步方法,并开始睡觉,1s
4 00:13:25 Thread-1 睡好了,但没事做,有事叫我,等待2s
5 00:13:26 Thread-0 我要走了,但我要再睡一觉,10s
6 00:13:36 Thread-0 走了
7 00:13:36 Thread-1 我要走了,但我要再睡一觉,10s
8 00:13:46 Thread-1 走了
*/
```
对输出做个简单解释(已经看懂代码的童鞋可以跳过):
```
1 00:13:23 Thread-0 进入了同步方法,并开始睡觉,1s // Thread-0首先进入同步方法,Thread-1只能门外候着
2 00:13:24 Thread-0 睡好了,但没事做,有事叫我,等待2s // Thread-0 sleep 1秒这段时间,Thread-1没进来,证明sleep没有释放锁
3 00:13:24 Thread-1 进入了同步方法,并开始睡觉,1s // Thread-0开始wait后Thread-1马上就进来了,证明wait释放了锁
4 00:13:25 Thread-1 睡好了,但没事做,有事叫我,等待2s // Thread-1也打算wait 2秒(2秒后真的能醒来吗?)
5 00:13:26 Thread-0 我要走了,但我要再睡一觉,10s // Thread-0已经wait超时醒来了,这次准备sleep 10s
6 00:13:36 Thread-0 走了 // 10s过去了Thread-0都sleep结束了,那个说要wait 2s的Thread-1还没动静,证明超时也没用,还得抢到锁
7 00:13:36 Thread-1 我要走了,但我要再睡一觉,10s // Thread-0退出同步代码后,Thread-1才终于得到了锁,能行动了
8 00:13:46 Thread-1 走了
```
## notify和notifyAll
同样是唤醒等待的线程,同样最多只有一个线程能获得锁,同样不能控制哪个线程获得锁。
区别在于:
+ notify:唤醒一个线程,其他线程依然处于wait的等待唤醒状态,如果被唤醒的线程结束时没调用notify,其他线程就永远没人去唤醒,只能等待超时,或者被中断
+ notifyAll:所有线程退出wait的状态,开始竞争锁,但只有一个线程能抢到,这个线程执行完后,其他线程又会有一个幸运儿脱颖而出得到锁
如果觉得解释的不够明白,代码来一波:
```java
import java.util.Date;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
private AtomicInteger count = new AtomicInteger();
public static void main(String[] args) throws InterruptedException {
Main main = new Main();
// 开启两个线程去执行test方法
for (int i = 0; i
探索JAVA并发 - 终于搞懂了sleep/wait/notify/notifyAll的更多相关文章
- java并发编程(十)使用wait/notify/notifyAll实现线程间通信
转载请注明出处:http://blog.csdn.net/ns_code/article/details/17225469 wait()方法:public final void wait() thr ...
- 终于搞懂了vue 的 render 函数(一) -_-|||
终于搞懂了vue 的 render 函数(一) -_-|||:https://blog.csdn.net/sansan_7957/article/details/83014838 render: h ...
- Java并发编程-看懂AQS的前世今生
在具备了volatile.CAS和模板方法设计模式的知识之后,我们可以来深入学习下AbstractQueuedSynchronizer(AQS),本文主要想从AQS的产生背景.设计和结构.源代码实现及 ...
- 干货:Java并发编程必懂知识点解析
本文大纲 并发编程三要素 原子性 原子,即一个不可再被分割的颗粒.在Java中原子性指的是一个或多个操作要么全部执行成功要么全部执行失败. 有序性 程序执行的顺序按照代码的先后顺序执行.(处理器可能会 ...
- Java并发编程:线程间通信wait、notify
Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
- [Java集合] 彻底搞懂HashMap,HashTable,ConcurrentHashMap之关联.
注: 今天看到的一篇讲hashMap,hashTable,concurrentHashMap很透彻的一篇文章, 感谢原作者的分享. 原文地址: http://blog.csdn.net/zhanger ...
- Java分布式锁,搞懂分布式锁实现看这篇文章就对了
随着微处理机技术的发展,人们只需花几百美元就能买到一个CPU芯片,这个芯片每秒钟执行的指令比80年代最大的大型机的处理机每秒钟所执行的指令还多.如果你愿意付出两倍的价钱,将得到同样的CPU,但它却以更 ...
- [转]我花了一个五一终于搞懂了OpenLDAP
轻型目录访问协议(英文:Lightweight Directory Access Protocol,缩写:LDAP)是一个开放的,中立的,工业标准的应用协议,通过IP协议提供访问控制和维护分布式信息的 ...
- 探索JAVA并发 - 可重入锁和不可重入锁
本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...
随机推荐
- [学习笔记] pymysql入门
一.快速开始 对于会用MySQL的朋友来说,开始使用pymysql可以说真的so esay,只要用下面的代码,把想要对数据库的操作放在 sql = " " 里就可以了. 没有接触过 ...
- 基于drone构建CI-CD系统
kubernetes集群三步安装 CI 概述 用一个可描述的配置定义整个工作流 程序员是很懒的动物,所以想各种办法解决重复劳动的问题,如果你的工作流中还在重复一些事,那么可能就得想想如何优化了 持续集 ...
- [原创]一款基于Reactor线程模型的java网络爬虫框架
AJSprider 概述 AJSprider是笔者基于Reactor线程模式+Jsoup+HttpClient封装的一款轻量级java多线程网络爬虫框架,简单上手,小白也能玩爬虫, 使用本框架,只需要 ...
- 基于 HTML5 WebGL 的加油站 3D 可视化监控
前言 随着数字化,工业互联网,物联网的发展,我国加油站正向有人值守,无人操作,远程控制的方向发展,传统的人工巡查方式逐渐转变为以自动化控制为主的在线监控方式,即采用数据采集与监控系统 SCADA.SC ...
- [译]Python中的异步IO:一个完整的演练
原文:Async IO in Python: A Complete Walkthrough 原文作者: Brad Solomon 原文发布时间:2019年1月16日 翻译:Tacey Wong 翻译时 ...
- Android:JNI与NDK(三)NDK构建的脚本文件配置
友情提示:欢迎关注本人公众号,那里有更好的阅读体验以及第一时间获取最新文章 本文目录 一.前言 本篇我们介绍Android.mk与CMakeLists.txt构建NDK的配置文件,我们知道目前NDK的 ...
- 教老婆学Linux运维(一)初识Linux
零.前言 之一 为什么写这个系列?为什么是Linux? 老婆自从怀孕以后,辞职在家待了好几年了,现在时常感觉与社会脱节.所以想找个工作. 做了多年程序员,有点人脉也都基本是在IT圈子里,只能帮忙找找I ...
- linux 配置ssh无密码登录不起作用的解决方案
1.安装ssh 直接 sudo apt-get install openssh-server 2.查看ssh运行状态 ps -e | grep ssh 如果发现 sshd 和 ssh-agent 即表 ...
- STM32CubeMX工程修改MCU的两种方法
有些时候我们在已经使用过一段时间的stm32cube创建的工程,需要更换一个同系列的芯片,比如Flash空间更大或者更小,第一种方法我在网上搜索过,就是使用cube选择一个新使用型号的MCU,然后使用 ...
- Android Bluetooth Low Energy (BLE)简单方便的蓝牙开源库——EasyBLE
源码传送门 最新版本 功能 支持多设备同时连接 支持广播包解析 支持连接同时配对 支持搜索系统已连接设备 支持搜索器设置 支持自定义搜索过滤条件 支持自动重连.最大重连次数限制.直接重连或搜索到设备再 ...