如果主线程想等待子线程执行完成之后再结束,就可以使用join方法了。它的使用是等待线程对象销毁。今天我们就通过实例来学习一下多线程中join方法的使用。草在结它的种子,风在摇它的叶子。我们站着,不说话,就十分美好。

Join方法的简单实例

一、Join方法的简单使用

import java.util.concurrent.TimeUnit;

public class MyThread implements Runnable {

    @Override
public void run() {
try {
int secondValue = (int) (Math.random() * 1000);
System.out.println(secondValue);
TimeUnit.MILLISECONDS.sleep(secondValue);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

  测试的主体类如下:

public class MyThreadTest {
public static void main(String[] args) {
try {
Thread thread = new Thread(new MyThread());
Thread thread2 = new Thread(new MyThread());
thread.start();
thread2.start();
thread.join();
thread2.join();
System.out.println("子线程MyThread执行完,我再执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

一次的运行结果如下:


子线程MyThread执行完,我再执行

  方法join的作用是使所属的线程对象x正常执行run()方法中的任务,而使当前线程Z进行无限期的阻塞,等待线程X销毁后再继续执行线程Z后面的代码。

二、对于join方法的分析

  我们首先写一个简单的测试类,来体会一下join方法的用法以及效果。我们在主线程中开启一下myThread的线程,在myThread线程里面会睡眠2秒,然后打印当前线程的名称。

package com.linux.huhx.thread;

import java.util.concurrent.TimeUnit;

/**
* Created by huhx on 2017-05-24.
*/
public class ThreadJoinTest2 {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
myThread.start();
System.out.println("thread name " + Thread.currentThread().getName());
} static class MyThread extends Thread {
boolean flag = true; @Override
public void run() {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("thread name " + Thread.currentThread().getName());
flag = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

  此时运行的结果如下,主线程里面的打印会先在控制台显示。两秒之后,打印myThread里面的字符。

thread name main
thread name Thread-

  我们修改代码,在main方法的myThread.start();方法后面加上myThread.join();控制台打印如下:

thread name Thread-0
thread name main

  了解了Thread的join方法的表现与效果之后,现在我们就这个join方法做一个理解与分析。以下是它的代码:

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;
}
}
}

  代码是比较简单的,由于我们的测试是myThread.join(),所以isAlive()方法与wait方法都是myThread类(其实也是类)调用的,不过他们运行在主线程中。也就是说while (isAlive()) 代码是判断myThread线程是否是活跃的,wait(0)方法虽说是myThread调用的,然而运行在main线程中,所以是主线程(current thread)等待其它的线程(这里面是myThread线程)在myThread类(for this object)调用notify或者notifyAll方法。

Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.

  那么现在问题来了,其它的线程(相对于main线程而言)是什么时候调用了notify或者notifyAll方法把主线程唤醒呢。下面我们修改上述的测试代码,把上述修改的myThread.join();代码为如下代码:

synchronized (myThread) {
while (myThread.flag) {
System.out.println("before flag = " + myThread.flag);
myThread.wait();
System.out.println("after flag = " + myThread.flag);
}
}

  现在运行程序,会有如下的结果。其实上述的代码和myThread.join方法的代码功能差不多。

before flag = true
thread name Thread-0
after flag = false
thread name main

  当主线程的程序运行到myThread.wait()的时候,它阻塞等待其它的线程在myThread对象上调用notify方法。在主线程等待的期间,myThread线程执行完毕之后,会有一个在当前线程类(myThread)的notifyAll的动作。作为对比,我们的代码修改如下:

Object object = new Object();
synchronized (object) {
while (myThread.flag) {
System.out.println("before flag = " + myThread.flag);
object.wait();
System.out.println("after flag = " + myThread.flag);
}
}

  由于是我们生成的object在主线程中调用wait方法,所以是synchronized (object)。我们可以看到主线程不会被唤醒,object.wait方法后面的代码不会得到执行。

  上面synchronized里面为什么是myThread呢,因为在主线程中是myThread对象调用的wait方法。如果修改成其它的,例如new Object()或者Object.class,会报错如下:

Exception in thread "main" java.lang.IllegalMonitorStateException
before flag = true
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.linux.huhx.thread.ThreadJoinTest2.main(ThreadJoinTest2.java:15)

  测试过程中积累的代码:

package com.linux.huhx.thread;

import java.util.concurrent.TimeUnit;

/**
* Created by huhx on 2017-05-24.
*/
public class ThreadJoinTest2 {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
myThread.start();
// join()
// myThread.join();
// while (myThread.flag) {
// synchronized(myThread) {
// Thread.currentThread().wait();
// }
// System.out.println(Thread.currentThread().getName());
// }
synchronized (myThread) {
while (myThread.flag) {
myThread.wait();
}
}
myThread.joinTest();
System.out.println("thread name " + Thread.currentThread().getName());
} static class MyThread extends Thread {
boolean flag = true; @Override
public void run() {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("thread name " + Thread.currentThread().getName());
flag = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
} public synchronized void joinTest() {
while (flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} }
}

友情链接

对于myThread是类和线程之间的关系,还是有些疑惑。就问题的本质而言,对于线程还是不能理解的很好。

java基础---->java多线程之Join(二)的更多相关文章

  1. Java基础-进程与线程之Thread类详解

    Java基础-进程与线程之Thread类详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.进程与线程的区别 简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程 ...

  2. java基础---->java中正则表达式二

    跟正则表达式相关的类有:Pattern.Matcher和String.今天我们就开始Java中正则表达式的学习. Pattern和Matcher的理解 一.正则表达式的使用方法 一般推荐使用的方式如下 ...

  3. 多线程之join方法

    join方法的功能就是使异步执行的线程变成同步执行.也就是说,当调用线程实例的start方法后,这个方法会立即返回,如果在调用start方法后后需要使用一个由这个线程计算得到的值,就必须使用join方 ...

  4. Java基础-Java中的内存分配与回收机制

    Java基础-Java中的内存分配与回收机制 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一. 二.

  5. Java基础-Java中的并法库之重入读写锁(ReentrantReadWriteLock)

    Java基础-Java中的并法库之重入读写锁(ReentrantReadWriteLock) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在学习Java的之前,你可能已经听说过读 ...

  6. Java基础-Java中的并法库之线程池技术

    Java基础-Java中的并法库之线程池技术 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是线程池技术 二.

  7. Java基础-Java中23种设计模式之常用的设计模式

    Java基础-Java中23种设计模式之常用的设计模式 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   一.设计模式分类 设计模式是针对特定场景给出的专家级的解决方案.总的来说设 ...

  8. Java基础-JAVA中常见的数据结构介绍

    Java基础-JAVA中常见的数据结构介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是数据结构 答:数据结构是指数据存储的组织方式.大致上分为线性表.栈(Stack) ...

  9. Java基础-Java数据类型

    Java基础-Java数据类型 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.数据类型的作用 数据类型就是一组值,以及这一组值上的操作,数据类型可以决定数据的存储方式,取值范围 ...

  10. Java基础-Java中的堆内存和离堆内存机制

    Java基础-Java中的堆内存和离堆内存机制 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.

随机推荐

  1. windows server 2008 域控安装

    windows server 2008 域控安装:1.dns安装,安装过程会提示.net frame 3.51安装 3.域控安装 原文:地址 http://wenku.baidu.com/link?u ...

  2. drupal7请求异常,执行时间过长的解决方法

    drupal7请求错误,执行时间过长的解决办法 根据你的系统或网络设置Drupal不能读取网页,造成功能缺失.可能是web服务器配置或PHP设置引起的,可用更新.获取更新源.使用OpenID登 录或使 ...

  3. 数据结构——算法之(043)(c++各种排序算法实现)

    [申明:本文仅限于自我归纳总结和相互交流,有纰漏还望各位指出. 联系邮箱:Mr_chenping@163.com] 题目: c++ 各种排序算法实现 题目分析: 详细排序原理參考相关算法书籍 算法实现 ...

  4. 看板与Scrum:哪个更适合你的团队?

    敏捷是理想型指标和原则,看板和Scrum是帮助团队坚持敏捷原则并完成工作的基本框架.本文详细介绍了在Scrum和看板之间做出选择时要考虑的关键因素,以及如果我们无法做出决定时该怎么办. Scrum和看 ...

  5. C++中前置声明的应用与陷阱

    前置声明的使用 有一定C++开发经验的朋友可能会遇到这样的场景:两个类A与B是强耦合关系,类A要引用B的对象,类B也要引用类A的对象.好的,不难,我的第一直觉让我写出这样的代码: // A.h #in ...

  6. thinkphp 3.2跟3.1 区别

    1.ThinkPHP3.2 主要整合了3.1以及之前版本的分组问题.3.2之前有普通分组和独立分组,还需要在配置文件中配置.3.2已经不需要配置了,直接使用独立分组就可以了.2.ThinkPHP3.2 ...

  7. 关于Cocos2d-x有些头文件无法引入或者类显示无法打开

    选中工程右键“属性”->"配置属性“->"c/c++"->"常规”->"附加包含目录"中添加“”$(EngineRo ...

  8. selenium测试(Java)--下拉框(二十一)

    例子: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title ...

  9. am335x hid-multitouch.c

    am335x在使用电容屏,需要加载hid-multitouch.ko模块 由下面文件生成 kernel/drivers/hid/hid-multitouch.c 内核中编译模块 make module ...

  10. csv导入mysql提示错误[Error Code] 1290 - The MySQL server is running with the --secure-file-priv option

    解决方法: 1.进入mysql查看secure_file_prive的值 mysql>SHOW VARIABLES LIKE "secure_file_priv"; secu ...