之前再看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()阻塞的线程?的更多相关文章

  1. JDK1.8 StampedLock: 解决ReentrantReadWriteLock在读多写少情况下,写线程饥饿问题

    ReentrantReadWriteLock 在沒有任何读写锁时,才可以取得写入锁,这可用于实现了悲观读取(Pessimistic Reading), 即如果执行中进行读取时,经常可能有另一执行要写入 ...

  2. 什么情况下可以不写PHP的结束标签“?>”

    我们经常看到有些PHP文件中的代码是只有开始标签,而没有结束标签的,那么什么情况下可以不写这个结束标签,而什么情况下必须写?先来看2个例子: 下面的代码正常运行: <?php echo 1234 ...

  3. MessagePack Java Jackson 在不关闭输出流(output stream)的情况下序列化多变量

    com.fasterxml.jackson.databind.ObjectMapper 在默认的情况下在写出输入后将会关闭输出流(output stream). 如果你希望序列化多值变量在同一个输出流 ...

  4. 关于socket阻塞与非阻塞情况下的recv、send、read、write返回值(转载)

    1.阻塞模式与非阻塞模式下recv的返回值各代表什么意思?有没有区别?(就我目前了解阻塞与非阻塞recv返回值没有区分,都是 <0:出错,=0:连接关闭,>0接收到数据大小,特别:返回值  ...

  5. UNIX网络编程——关于socket阻塞与非阻塞情况下的recv、send、read、write返回值

    1.阻塞模式与非阻塞模式下recv的返回值各代表什么意思?有没有 区别?(就我目前了解阻塞与非阻塞recv返回值没有区分,都是 <0:出错,=0:连接关闭,>0接收到数据大小,特别:返回 ...

  6. 关于socket阻塞与非阻塞情况下的recv、send、read、write返回值---部分内容可能不确切,待讨论

    1.阻塞模式与非阻塞模式下recv的返回值各代表什么意思?有没有区别?(就我目前了解阻塞与非阻塞recv返回值没有区分,都是 <0:出错,=0:连接关闭,>0接收到数据大小,特别:返回值  ...

  7. 如何使用wait(), notify() and notifyAll() – Java

    Java多线程是个很复杂的问题,尤其在多线程在任何给定的时间访问共享资源需要更加注意.Java 5引入了一些类比如BlockingQueue 和Executors 类提供了易于使用的API,避免了一些 ...

  8. JAVA线程同步 (二)notify()与notifyAll()-***

    编写多线程程序需要进行线程协作,前面介绍的利用互斥来防止线程竞速是来解决线程协作的衍生危害的.编写线程协作程序的关键是解决线程之间的协调问题,在这些任务中,某些可以并行执行,但是某些步骤需要所有的任务 ...

  9. 最简实例说明wait、notify、notifyAll的使用方法

    wait().notify().notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态. 这三个方法最终调用的都是jvm级的native方法.随着jvm运行平台的不同可能有些 ...

随机推荐

  1. 《自拍教程9》Python编程风格规范

    Python编程风格规范 根据Python官方提供的Python编程风格规范: Style Guide for Python Code, 即PEP8规范, https://www.python.org ...

  2. Cacti 升级

    现在用的 cacti 1.0.3   决定升级一下cacti到最新版本 1.1.1   官方升级指导文件 Upgrading Cacti Backup the old Cacti database. ...

  3. windows设置开机自启动的地方

    2013-03-24 11:06 (分类:网络安全) 精心总结,这些都是可以放小木马的好地方,留意了 1.最简单的 开始→程序→启动它的位置 C:\Documents and Settings\*** ...

  4. SAXParseException Content is not allowed in Prolog (前言中不允许有内容)

    解析 XML 文件的时候,如 Mybatis 的 Mapper 文件,有时会出现 org.xml.sax.SAXParseException 前言中不允许有内容 的异常,英文就是 Content is ...

  5. HDU 1017 直接暴力。

    C - 3 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status ...

  6. 授权认证(IdentityServer4)

    区别 OpenId: Authentication :认证 Oauth: Aurhorize :授权 输入账号密码,QQ确认输入了正确的账号密码可以登录 --->认证 下面需要勾选的复选框(获取 ...

  7. vue框架中props的typescript用法

    vue框架中props的typescript用法 在vue中使用typescript时,需要引入vue-property-decorator库来兼容格式. javascript写法 Vue.compo ...

  8. 利用低代码优化人力资源配置,为软件开发降本提效 ZT

    低代码 是一种主要应用于企业信息化领域的快速开发技术.借助低代码,开发者无需编码即可生成企业应用的常见功能,少量编码能开发出更多扩展功能.有了低代码技术,IT团队甚至业务团队都可以参与到编写应用程序当 ...

  9. C#中FolderBrowserDialog类打开文件夹使用说明

    C#中FolderBrowserDialog类打开文件夹使用说明   作用:打开文件选择窗口获取文件夹路径.   导入的命名空间为: System.Windows.Forms; 属性:   Descr ...

  10. python-21-生成器又是什么东西?

    前言 生成器,只要含有yield关键字的函数都是生成器函数,但yield不能和return共用且需要写在函数内. 生成器,是返回一个迭代器的函数,说白了生成器也是迭代器. 一.生成器简介 1.只要含有 ...