博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
颠覆我的Thread.join()
阅读量:5849 次
发布时间:2019-06-19

本文共 6052 字,大约阅读时间需要 20 分钟。

学而时习之,不亦说乎!

                             --《论语》

  为什么说是颠覆?

  1)任何对象都可以作为锁对象,锁对象的行为都是一样的吗?之前我一直认为锁对象的方法都是定义在Object类中,而所有类都是Object的子类,这些方法又都是native方法,那么用哪个对象作为锁对象又有什么区别呢?

  2)一个线程对象a在run()方法内部调用线程对象b的join()方法,那么是将b线程加入,等到b线程执行完毕再执行a线程?那么如果还有一个正在执行的c线程呢,线程c也会等待b执行完吗?

代码1:

package com.zby;public class Application1 {    public static void main(String[] args) {        Thread prepare = new Thread(new Runnable() {            public void run() {                for (int i = 0; i < 5; i++) {                    System.out.println("Hello,World!-----" + i);                    try {                        Thread.sleep(500);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        });        prepare.start();        System.out.println("Hello,ZBY!");    }}

控制台输出:

Hello,ZBY!Hello,World!-----0Hello,World!-----1Hello,World!-----2Hello,World!-----3Hello,World!-----4

结果不难分析,主线程直接执行,而prepare线程虽然启动了,但是执行没那么快,所以后执行了。但是我的prepare是进行准备工作的,我想让prepare线程执行完毕后再执行主线程。

代码2:

package com.zby;public class Application2 {    public static void main(String[] args) {        Thread prepare = new Thread(new Runnable() {            public void run() {                for (int i = 0; i < 5; i++) {                    System.out.println("Hello,World!-----" + i);                    try {                        Thread.sleep(500);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        });        prepare.start();        try {            prepare.join();        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("Hello,ZBY!");    }}

控制台输出:

Hello,World!-----0Hello,World!-----1Hello,World!-----2Hello,World!-----3Hello,World!-----4Hello,ZBY!

很小儿科,加了一个一行代码:prepare.join();要是之前我会理解成把prepare加入到主线程先执行,执行完才能执行其它线程。然而,非也。

Thread.join()源代码:

public final void join() throws InterruptedException {        join(0);    }    public final synchronized void join(long millis)    throws InterruptedException {        long base = System.currentTimeMillis();        long now = 0;        if (millis < 0) {            throw new IllegalArgumentException("timeout value is negative");        }        if (millis == 0) {            while (isAlive()) {                wait(0);            }        } else {            while (isAlive()) {                long delay = millis - now;                if (delay <= 0) {                    break;                }                wait(delay);                now = System.currentTimeMillis() - base;            }        }    }

这儿可以看出来,我们调用prepare.join()的时候发生了什么:把prepare作为锁对象,调用锁对象的wait(0)方法,阻塞当前线程!也就是说,并不是把需要执行的线程加入进来让他先执行,而是阻塞当前的线程!那么,如果还有第三个线程也在执行,那么prepare线程是不会一直执行,而是跟第三个线程抢CPU执行权。总结起来就是,其实就是阻塞了当前的线程,对于其他线程都是没有影响的。感兴趣可以加入第三个线程自己测试一下。

等价于代码2的代码3:

package com.zby;public class Application3 {    public static void main(String[] args) {        Thread prepare = new Thread(new Runnable() {            public void run() {                for (int i = 0; i < 5; i++) {                    System.out.println("Hello,World!-----" + i);                    try {                        Thread.sleep(500);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        });        prepare.start();        synchronized(prepare){            try {                prepare.wait(0);            } catch (InterruptedException e) {                e.printStackTrace();            }        }        System.out.println("Hello,ZBY!");    }}

控制台输出:

Hello,World!-----0Hello,World!-----1Hello,World!-----2Hello,World!-----3Hello,World!-----4Hello,ZBY!

这儿就解决了第二个问题,join()方法其实不是把调用的线程加入进来优先执行,而是阻塞当前线程!

看完代码3就有疑问了,prepare.wait(0);自然没错,阻塞住了主线程。但是并没有任何地方调用notify或者notifyAll方法,线程不是应该一直阻塞么,怎么会在prepare执行完后继续执行主线程代码了?

这就是第一个问题了,普通对象当然是wait后必须等待notify唤醒才能继续执行,但是Thread对象呢?具体的我也不知道,但是从这儿可以推论出,thread对象在执行完毕后,自动唤醒了!那么到底是notify还是notifyAll呢?那么,多启动一个线程,并使用prepare对象作为锁对象,调用wait方法。

代码4:

package com.zby;public class Application3 {    public static void main(String[] args) {        final Thread prepare = new Thread(new Runnable() {            public void run() {                for (int i = 0; i < 5; i++) {                    System.out.println("Hello,World!-----" + i);                    try {                        Thread.sleep(500);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        });        prepare.start();        Thread ready = new Thread(new Runnable() {            public void run() {                synchronized (prepare) {                    try {                        prepare.wait(0);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }                for (int i = 0; i < 10; i++) {                    System.out.println("Hello,Earth!-----" + i);                    try {                        Thread.sleep(500);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        });        ready.start();        synchronized (prepare) {            try {                prepare.wait(0);            } catch (InterruptedException e) {                e.printStackTrace();            }        }        System.out.println("Hello,ZBY!");    }}

控制台输出:

Hello,World!-----0Hello,World!-----1Hello,World!-----2Hello,World!-----3Hello,World!-----4Hello,Earth!-----0Hello,ZBY!Hello,Earth!-----1Hello,Earth!-----2Hello,Earth!-----3Hello,Earth!-----4Hello,Earth!-----5Hello,Earth!-----6Hello,Earth!-----7Hello,Earth!-----8Hello,Earth!-----9

可以看出,在preare执行完之后,自动把ready和main线程都唤醒了!也就是说,使用Thread对象作为锁对象,在Thread执行完成之后,会唤醒使用该对象作为锁对象调用wait()休眠的线程。

总结:

1)使用线程的join方法,是将调用的线程对象作为锁对象,阻塞当前线程,不影响其他线程的运行。

2)推论:Thread对象作为线程锁对象,会在Thread对象执行完后,调用Thread对象的notifyAll方法。

转载于:https://www.cnblogs.com/zby9527/p/7526606.html

你可能感兴趣的文章
白帽子技术分析会话劫持实战讲解
查看>>
我的友情链接
查看>>
yum的三种方式
查看>>
Redis分布式缓存安装和使用
查看>>
PHP环境搭建:Windows 7下安装配置PHP+Apache+Mysql环境教程以及注意事项
查看>>
20天精通 Windows 8:系列课程资料集
查看>>
html5 <figure> 标签
查看>>
linux的I/O多路转接select的fd_set数据结构和相应FD_宏的实现分析
查看>>
Mysql数据库InnoDB存储引擎的隔离级别
查看>>
开源监控软件 Hyperic 的两种插件
查看>>
TOMCAT
查看>>
无土栽培中的物联网技术应用
查看>>
div contenteditable="true"各个浏览器上的解析
查看>>
Spark学习记录(二)Spark集群搭建
查看>>
Python基本数据类型之字典
查看>>
php引用(&)详解及注意事项
查看>>
OSChina 周一乱弹 —— 只要给网,这种生活我能过一辈子
查看>>
短信猫JAVA二次开发包SMSLib,org.smslib.TimeoutException: No response from device解决方案...
查看>>
CloudStack 4.4学习总结之cloudstack-management安装
查看>>
【动弹有奖】——OSC登录并发送动弹分析(附python源码)
查看>>