博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java线程的join()方法浅析
阅读量:4113 次
发布时间:2019-05-25

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

1.join()的作用

调用某个线程的join()方法时,这个方法会挂起调用线程,直到被调用线程结束执行,调用线程才会继续执行。

2.放上join()的源码

/**     * Waits for this thread to die.     *     * 

An invocation of this method behaves in exactly the same * way as the invocation * *

* {@linkplain #join(long) join}{@code (0)} *
* * @throws InterruptedException * if any thread has interrupted the current thread. The * interrupted status of the current thread is * cleared when this exception is thrown. */ public final void join() throws InterruptedException { join(0); }
/**     * Waits at most {@code millis} milliseconds for this thread to     * die. A timeout of {@code 0} means to wait forever.     *     * 

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. * * @param millis * the time to wait in milliseconds * * @throws IllegalArgumentException * if the value of {@code millis} is negative * * @throws InterruptedException * if any thread has interrupted the current thread. The * interrupted status of the current thread is * cleared when this exception is thrown. */ 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; } } }

/**     * Waits at most {@code millis} milliseconds plus     * {@code nanos} nanoseconds for this thread to die.     *     * 

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. * * @param millis * the time to wait in milliseconds * * @param nanos * {@code 0-999999} additional nanoseconds to wait * * @throws IllegalArgumentException * if the value of {@code millis} is negative, or the value * of {@code nanos} is not in the range {@code 0-999999} * * @throws InterruptedException * if any thread has interrupted the current thread. The * interrupted status of the current thread is * cleared when this exception is thrown. */ public final synchronized void join(long millis, int nanos) throws InterruptedException { if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos >= 500000 || (nanos != 0 && millis == 0)) { millis++; } join(millis); }

3.分析

1)join()共有三个重载版本,join()和join(long millis, int nanos)最后都调用了join(long millis)。

2)带参数的 join() 都是 synchronized方法。

3)join() 调用了 join(0),从源码可以看到 join(0) 不断检查当前线程(join() 所属的线程实例,非调用线程)是否是 Active。如果被调用线程的对象生成了,但是未启动,调用它的join()是没有作用的,将继续向下执行。

4)当一个线程调用另一个线程的join()时,会先获取到被调用线程的锁,然后调用wait方法释放掉被调用线程的锁。

5)join()具有使线程排队运行的作用,有些类似同步的运行效果,join()与synchronized的区别是:join()在内部使用wait()方法进行等待,而synchronized关键字使用的是“对象监视器”原理作为同步。

6)join() 和 sleep() 一样,都可以被中断(被中断时,会抛出 InterrupptedException 异常);不同的是,join() 内部调用了 wait(),会释放锁,而 sleep() 会一直持有锁,如下所示:

类ThreadB.java代码如下:

package com.smile13.joinandsleep;/** * Created by eric on 2018/7/31. */public class ThreadB extends Thread {	@Override	public void run() {		try {			System.out.println(" b run begin thimer = " + System.currentTimeMillis());			Thread.sleep(5000);			System.out.println(" b run end thimer = " + System.currentTimeMillis());		} catch (InterruptedException e) {			e.printStackTrace();		}	}	public synchronized void bService() {		System.out.println("打印了 bService timer = " + System.currentTimeMillis());	}}

类ThreadA.java代码如下:

package com.smile13.joinandsleep;/** * Created by eric on 2018/7/31. */public class ThreadA extends Thread {	private ThreadB b;	public ThreadA(ThreadB b) {		super();		this.b = b;	}	@Override	public void run() {		try {			synchronized (b) {				b.start();				Thread.sleep(6000); // 不释放锁			}		} catch (InterruptedException e) {			e.printStackTrace();		}	}}

类ThreadC.java代码如下:

package com.smile13.joinandsleep;/** * Created by eric on 2018/7/31. */public class ThreadC extends Thread {	private ThreadB threadB;	public ThreadC(ThreadB threadB) {		super();		this.threadB = threadB;	}	@Override	public void run() {		threadB.bService();	}}

类Run.java代码如下:

package com.smile13.joinandsleep;/** * Created by eric on 2018/7/31. */public class Run {	public static void main(String[] args) {		try {			ThreadB b = new ThreadB();			ThreadA a = new ThreadA(b);			a.start();			Thread.sleep(1000);			ThreadC c = new ThreadC(b);			c.start();		} catch (InterruptedException e) {			e.printStackTrace();		}	}}

程序Run.java运行后的效果如下所示:

b run begin thimer = 1533050891103 b run end thimer = 1533050896103打印了 bService timer = 1533050897103

说明:由于线程ThreadA使用Thread.sleep(long millis)方法一直持有ThreadB对象的锁,时间达到6秒,所以线程ThreadC只有在ThreadA时间达到6秒后释放ThreadB对象的锁时,才能调用ThreadB中的同步方法bService(),上面的实验证明Thread.sleep(long millis)不会释放锁

下面修改ThreadA的代码,验证join()方法会释放锁。

修改ThreadA.java类的代码如下:

package com.smile13.joinandsleep;/** * Created by eric on 2018/7/31. */public class ThreadA extends Thread {	private ThreadB b;	public ThreadA(ThreadB b) {		super();		this.b = b;	}	@Override	public void run() {		try {			synchronized (b) {				b.start();				b.join(); // 说明join()释放锁				System.out.println("线程b执行完成,线程a继续执行...");			}		} catch (InterruptedException e) {			e.printStackTrace();		}	}}

程序Run.java运行后的效果如下所示:

b run begin thimer = 1533052328339打印了 bService timer = 1533052329340 b run end thimer = 1533052333340线程b执行完成,线程a继续执行...

说明:由于线程ThreadA释放了ThreadB的锁,所以线程ThreadC可以调用ThreadB的同步方法bService(),此实验说明join()方法具有释放锁的特点。

4.未完待续....

 

 

 

转载地址:http://bdgsi.baihongyu.com/

你可能感兴趣的文章
Golang面试考题记录 ━━ 只出现一次的数字,学习异或^=处理
查看>>
Golang面试考题记录 ━━ 两个数组的交集 II 双100%及goto、continue和break的用法
查看>>
Golang面试考题记录 ━━ 加一,暂无执行排名
查看>>
Golang面试考题记录 ━━ 移动零100%务必深刻理解方法三
查看>>
Golang面试考题记录 ━━ 两数之和 ,能一遍循环就一遍循环
查看>>
Golang面试考题记录 ━━ 旋转图像~~二维数组旋转90度
查看>>
Golang面试考题记录 ━━ 有效的数独,没发现什么特别好的算法,就是暴力,结果也差不多
查看>>
Golang面试考题记录 ━━ 反转字符串,一种思路几种细节的不同结果
查看>>
Golang面试考题记录 ━━ 整数反转 解答及扩展的三个知识点
查看>>
Golang面试考题记录 ━━ 字符串中的第一个唯一字符 ,拓展:ASCII和strings字符串查找的用法
查看>>
Golang面试考题记录 ━━ 有效的字母异位词,久违的双100%,拓展reflect.DeepEqual()用法和[26]int{}的值
查看>>
Golang面试考题记录 ━━ 验证回文串,多种方法涉及双指针、strings、unicode和regexp
查看>>
Golang面试考题记录 ━━ 字符串转换整数 (atoi),知识点ascii、rune、uint8、string、char等转换
查看>>
Golang面试考题记录 ━━ 实现 strStr() 函数,截然不同三种方案,效率都差不多,双100%
查看>>
Golang面试考题记录 ━━ 外观数列 , 了解递归、bytes.Buffer和闭包
查看>>
学习日志 ━━ 理解递归(使用go语法举例)
查看>>
Golang面试考题记录 ━━ 最长公共前缀,字符串就是切片,复习[]byte、[]rune、[]uint8、[]int32和单引号
查看>>
Golang学习日志 ━━ 单向链表
查看>>
Golang面试考题记录 ━━ 删除链表中的节点,首先明白什么是链表,其次语文要好能看懂题
查看>>
Golang面试考题记录 ━━ 删除链表的倒数第N个节点, 学习闭包递归、双指针、哨兵节点
查看>>