颠覆我的Thread.join()
学而时习之,不亦说乎!
--《论语》
为什么说是颠覆?
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!-----0
Hello,World!-----1
Hello,World!-----2
Hello,World!-----3
Hello,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!-----0
Hello,World!-----1
Hello,World!-----2
Hello,World!-----3
Hello,World!-----4
Hello,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!-----0
Hello,World!-----1
Hello,World!-----2
Hello,World!-----3
Hello,World!-----4
Hello,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!-----0
Hello,World!-----1
Hello,World!-----2
Hello,World!-----3
Hello,World!-----4
Hello,Earth!-----0
Hello,ZBY!
Hello,Earth!-----1
Hello,Earth!-----2
Hello,Earth!-----3
Hello,Earth!-----4
Hello,Earth!-----5
Hello,Earth!-----6
Hello,Earth!-----7
Hello,Earth!-----8
Hello,Earth!-----9
可以看出,在preare执行完之后,自动把ready和main线程都唤醒了!也就是说,使用Thread对象作为锁对象,在Thread执行完成之后,会唤醒使用该对象作为锁对象调用wait()休眠的线程。
总结:
1)使用线程的join方法,是将调用的线程对象作为锁对象,阻塞当前线程,不影响其他线程的运行。
2)推论:Thread对象作为线程锁对象,会在Thread对象执行完后,调用Thread对象的notifyAll方法。
颠覆我的Thread.join()的更多相关文章
- thread.join 从异步执行变成同步
Java的线程模型为我们提供了更好的解决方案,这就是join方法.在前面已经讨论过,join的功能就是使用线程 从异步执行变成同步执行 当线程变成同步执行后,就和从普通的方法中得到返回数据没有什么区别 ...
- Thread.join()方法
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程.比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B.t.join() ...
- Thread .join 的用法一例
在使用身份证读卡器时,要求 1. 身份证读到身份证 就 停止线程. 2. 关闭界面时会 自动停止调用读身份证的线程.这时候就需要用到 Thead.join 例子如下: Thread thread; p ...
- Part 92 Significance of Thread Join and Thread IsAlive functions
Thread.Join & Thread.IsAlive functions Join blocks the current thread and makes it wait until th ...
- [译]Java Thread join示例与详解
Java Thread join示例与详解 Java Thread join方法用来暂停当前线程直到join操作上的线程结束.java中有三个重载的join方法: public final void ...
- 多线程编程(一) - 关于C#中Thread.Join()
Thread.Join()在MSDN中的解释很模糊:Blocks the calling thread until a thread terminates 有两个主要问题:1.什么是the calli ...
- 关于C#中Thread.Join()的一点理解
原文地址:http://www.cnblogs.com/slikyn/articles/1525940.html 今天是第一次在C#中接触Thread,自己研究了一下其中Thread.Join()这个 ...
- C#中Thread.Join()的理解
最近在项目中使用多线程,但是对多线程的一些用法和概念还有有些模棱两可,为了搞清楚查阅了一写资料,写下这篇日志加深理解吧. Thread.Join()在MSDN中的解释很模糊:Blocks the ca ...
- Thread.join()分析方法
API: join public final void join() throws InterruptedException 等待该线程终止. 抛出: InterruptedException - 假 ...
随机推荐
- console对象的理解
console对象代表浏览器的JS控制台,虽然不是标准,但已经成为事实的标准 console对象的作用 a.显示代码运行错误信息 b.提供一个命令行接口,用来与网页互动 console对象拥有很多方法 ...
- 4.3.1 ThreadLoacl简单使用
我们都知道 SimpleDateFormat 这个类是线程 不安全的,那么我下面的程序执行就会遇到问题 public class ParseDateDemo { private static fin ...
- yii2 Html::a
Html::a($text,$url = null,$options = []) $url 可以直接是字符串 // An empty string. This will return the curr ...
- OpenSSH免密码登录SSH2
SSH2免密码登录OpenSSHhttp://blog.csdn.net/aquester/article/details/23836299 两个SSH2间免密码登录http://blog.csdn. ...
- .NET基础 (01).NET基础概念
.NET基础概念 1 什么是CTS.CLS和CLR2 开发和运行.NET程序需要的最基本环节是什么3 .NET是否支持多编程语言开发4 CLR技术和COM技术的比较5 什么是程序集和应用程序域 1 什 ...
- Spring学习(一)——环境准备
以前做的项目都是用.net开发的,以后准备迁移到java平台上,近期正好有个新项目要上马,所以调研下java相关技术.Spring作为java平台下的一个全栈框架, 其简洁优雅的设计和 ...
- ASP.NET MVC 全局过滤器(FilterConfig)、标记在控制器上和方法上的筛选器执行顺序
FilterConfig->控制器上的筛选器-->方法上的筛选器(大-->小,上-->下) 全局-->控制器->个别 尝试的时候记得把返回true protecte ...
- (zxing.net)一维码Code 39的简介、实现与解码
一.简介 一维码Code 39:由于编制简单.能够对任意长度的数据进行编码.支持设备广泛等特性而被广泛采用. Code 39码特点: 能够对任意长度的数据进行编码,其局限在于印刷品的长度和条码阅读器的 ...
- How to use the NFS Client c# Library
类库下载 I add a wiki page that explains how to use the NFS Client c# .net library in your project. Neko ...
- 【QTP小技巧】02_QTP中Complete Word 实现(转载)
相信做过开发或者写过JAVA的朋友都应该对Complete Word这个词语不陌生吧~ 对~~~它就是开发脚本过程中的催化剂 有了它我们就可以不用去死记硬背 有了它我们就不会出现某个代码少一个字母 有 ...