在什么情况下,不写notify()或者notifyAll()就能唤醒被wait()阻塞的线程?
之前再看java关于线程的某视频时,发现在JDK源码中,join()=join(0)=wait()=wait(0),但是视频中在join()了之后,并没有用notify()或者notifyAll()去唤醒,遂有了一个疑问:**在什么情况下,不写notify()或者notifyAll()就能唤醒被wait()阻塞的线程? **
**以下是思考的过程,如果不想看,可以直接跳到 总结。 **
**测试代码如下: **
main类
import java.lang.*;
public class Main {
public static void main(String[] args) {
new ThreadOne().start();
}
}
ThreadOne类
public class ThreadOne extends Thread{
@Override
public void run(){
System.out.println("1 start");
ThreadTwo t2 = new ThreadTwo();
t2.start();
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1 end");
}
}
ThreadTwo类
public class ThreadTwo extends Thread{
@Override
public synchronized void run() {
System.out.println("2 start");
System.out.println("2 end");
}
}
按理说,t1线程在调用t2的join()(在底层中相当于wait())之后,应该是被阻塞的。按照之前学的知识,wait()之后需要notify()或者notifyAll()才会被被唤醒,然而运行结果是这样的:
也就是说,代码中我并没有使用notify()或者notifyAll(),但是t1还是被唤醒了,是被谁唤醒的呢?
对此我有了初步猜测:t1线程在调用t2的wait()之后,被阻塞,在t2自己运行完run()了之后,会隐式调用notify()或者notifyAll(),去唤醒被t2的wait()阻塞的线程。
有了猜测就开始去调研啦
先是查询了一些博客:
https://blog.csdn.net/nmyangym/article/details/7850882
https://blog.csdn.net/Deronn/article/details/80450959
博客做过一些实验后,结论如下,和我的猜测差不多:
1.线程对象的wait()方法运行后,可以不用其notify()方法退出,会在线程结束后,自动退出。
2.线程间的等待唤醒机制,最好不要用线程对象做同步锁!
随后又去查看了JDK的源码,join(long millis)的注释是这样的:
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
*
* <p> This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
其中的As a thread terminates the {@code this.notifyAll} method is invoked.
告诉我们:一个线程在结束后,会调用notifyAll()方法。证实了我的初步猜测,隐式调用了notifyAll()方法。
但是在join(long millis)的代码实现中,告诉了我们join(0)实际上是调用了wait(0)方法。
if (millis == 0) {
while (isAlive()) {
wait(0);
}
}
我能不能追究到更细节的一些东西呢?然后我又去看了看wait(long timeout)的源码,它是个native方法,无法直接查看源码。
没法看wait()的源码咯,那就看看注释吧,然而注释并没能解决我的问题,好吧,就不钻牛角尖了。问题到此结束。
总结
**遇上的问题:**
众所周知,wait()之后要用notify()或者notifyAll()把线程从等待状态转为就绪状态。随后发现在底层JDK源码中,join()=join(0)=wait()=wait(0),但是在我的代码展示中,线程t1调用t2的wait()方法被阻塞后,并没有用notify()或者notifyAll()去唤醒,但是t1还是被唤醒了,究竟是谁唤醒的呢?
初步猜测:
t1线程在调用t2的wait()之后,被阻塞,在t2自己运行完run()了之后,会隐式调用notify()或者notifyAll(),去唤醒被t2的wait()阻塞的线程。
调研过程:
经过了查博客,查JDK源码(join()和wait())之后,发现在join()的注释里有这么一句话。As a thread terminates the {@code this.notifyAll} method is invoked.告诉我们:一个线程在结束后,会调用notifyAll()方法。
收获:
1.线程对象的wait()方法运行后,可以不用其notify()方法退出,因为一个线程在结束后,会调用notifyAll()方法。
2.线程间的等待唤醒机制,最好不要用线程对象做同步锁!
3.java源码中的native方法是不能直接在jdk中看到的,因为jdk不是开源的,要看到的话需要sun授权才行,现在只有openjdk是被sun公司授权。
4.博客园的markdown换行方式竟然不支持连续两个以上空格+回车
在什么情况下,不写notify()或者notifyAll()就能唤醒被wait()阻塞的线程?的更多相关文章
- JDK1.8 StampedLock: 解决ReentrantReadWriteLock在读多写少情况下,写线程饥饿问题
ReentrantReadWriteLock 在沒有任何读写锁时,才可以取得写入锁,这可用于实现了悲观读取(Pessimistic Reading), 即如果执行中进行读取时,经常可能有另一执行要写入 ...
- 什么情况下可以不写PHP的结束标签“?>”
我们经常看到有些PHP文件中的代码是只有开始标签,而没有结束标签的,那么什么情况下可以不写这个结束标签,而什么情况下必须写?先来看2个例子: 下面的代码正常运行: <?php echo 1234 ...
- MessagePack Java Jackson 在不关闭输出流(output stream)的情况下序列化多变量
com.fasterxml.jackson.databind.ObjectMapper 在默认的情况下在写出输入后将会关闭输出流(output stream). 如果你希望序列化多值变量在同一个输出流 ...
- 关于socket阻塞与非阻塞情况下的recv、send、read、write返回值(转载)
1.阻塞模式与非阻塞模式下recv的返回值各代表什么意思?有没有区别?(就我目前了解阻塞与非阻塞recv返回值没有区分,都是 <0:出错,=0:连接关闭,>0接收到数据大小,特别:返回值 ...
- UNIX网络编程——关于socket阻塞与非阻塞情况下的recv、send、read、write返回值
1.阻塞模式与非阻塞模式下recv的返回值各代表什么意思?有没有 区别?(就我目前了解阻塞与非阻塞recv返回值没有区分,都是 <0:出错,=0:连接关闭,>0接收到数据大小,特别:返回 ...
- 关于socket阻塞与非阻塞情况下的recv、send、read、write返回值---部分内容可能不确切,待讨论
1.阻塞模式与非阻塞模式下recv的返回值各代表什么意思?有没有区别?(就我目前了解阻塞与非阻塞recv返回值没有区分,都是 <0:出错,=0:连接关闭,>0接收到数据大小,特别:返回值 ...
- 如何使用wait(), notify() and notifyAll() – Java
Java多线程是个很复杂的问题,尤其在多线程在任何给定的时间访问共享资源需要更加注意.Java 5引入了一些类比如BlockingQueue 和Executors 类提供了易于使用的API,避免了一些 ...
- JAVA线程同步 (二)notify()与notifyAll()-***
编写多线程程序需要进行线程协作,前面介绍的利用互斥来防止线程竞速是来解决线程协作的衍生危害的.编写线程协作程序的关键是解决线程之间的协调问题,在这些任务中,某些可以并行执行,但是某些步骤需要所有的任务 ...
- 最简实例说明wait、notify、notifyAll的使用方法
wait().notify().notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态. 这三个方法最终调用的都是jvm级的native方法.随着jvm运行平台的不同可能有些 ...
随机推荐
- 如何开始阅读ASP.NET Core源代码
背景 当我们对ASP.Net Core内部的某些方法.类的实现感兴趣时,有很多方法可以去了解,看书,看各种文章,但是最直接也是最深入的办法就是去阅读源代码.ASP.NET Core的源代码托管在Git ...
- golang 自定义结构体(与其他语言对象类似)
/* 结构体变量: 结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存, 因此必须在定义结构体并实例化后才能使用结构体的字段. type 类型名 struct { 字段1 字 ...
- linux下的cron定时任务知识梳理
1 cron定时任务 1.1 cron介绍 为什么需要cron定时任务? 1)cron服务在安装完Linux系统后就默认就存在,主要用来定期执行命令或定期执行指定的应用程序; 2)cron服务默认情况 ...
- [Linux]curl 测试socket http代理
Socket5 curl --socks5 10.2.46.19:10808 http://www.baidu.com #-----查询结果----- <!DOCTYPE html> &l ...
- 第一篇 Springboot + Web MVC + MyBatis + 简单UI + Thymeleaf实现
源码链接:https://pan.baidu.com/s/1-LtF56dnCM277v5lILRM7g 提取码:c374 第二篇 Springboot mybatis generate根据数据库表自 ...
- 实验一 GIT 代码版本管理
实验一 GIT 代码版本管理 实验目的: 1)了解分布式分布式版本控制系统的核心机理: 2)熟练掌握git的基本指令和分支管理指令: 实验内容: 1)安装git 2)初始配置git ,git ini ...
- 在window上安装mysql - MySQL5.7.24 版本
1.下载安装包 下载地址:https://dev.mysql.com/downloads/mysql/5.7.html#downloads 点击Download,选择 No thanks, just ...
- 修改计算机名并更新sqlserver中存储的服务器名称
1. 查看计算机名use master go select @@servername select serverproperty('servername') 2.同步更新SQLse ...
- 解决visual studio 2013编译过程中存在的无法打开kernel.lib问题
1. 出现此类问题的原因 由于原visual studio文件中的安装中出现问题,所以原有的SDK(soft development kits)文件出现缺失: 2. 解决方法1 重新下载SDK工具,安 ...
- MNIST 源码解析
# Copyright 2015 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 ...