博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
join(long)与sleep(long)的区别
阅读量:6201 次
发布时间:2019-06-21

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

hot3.png

1.join(long)方法的源码

首先看join()源码:

 public final void join() throws InterruptedException {	join(0);    }

从源码中可以看出,join()直接调用了join(long)方法,join(long)源码如下:

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

通过阅读源码可以看出,是通过不断轮询的方式去观察线程是否还是活动线程

join(0)是不断的询问线程的状态,直到线程销毁join()方法才会结束。

当millis>0时,不在是wait(0),而是wait(delay),delay是还剩余的millis时间。有人会问第一次wait(delay)后不就已经经过这millis的时间,为什么还会有while执行多次wait(delay)呢?因为这里不仅要考虑wait在delay时间后被唤醒,还要考虑到在delay时间内,被notify唤醒,唤醒后还没有执行到millis的时间,因此要多次调用wait(delay)方法。

2.比较join(long) 与 sleep(long),join方法后语句提前执行问题

join方法底层调用的是wait方法,执行到wait方法可以释放锁,而sleep方法不释放锁

具体细节通过案例来讲解:

首先定义两个线程类:MyThreadA,MyThreadB,其中为了保证两个线程可以同步执行,在MyThreadA中添加一个MyThreadB的实例变量,具体代码如下:

package com.feng.example;public class MyThreadB extends Thread {	@Override	synchronized public void run() {		try {			System.out.println("begin B thread, ThreadName="+Thread.currentThread().getName()+"==="+System.currentTimeMillis());			Thread.sleep(5000);			System.out.println("end B thread, ThreadName="+Thread.currentThread().getName()+"==="+System.currentTimeMillis());		} catch (InterruptedException e) {			// TODO Auto-generated catch block			e.printStackTrace();		}	}}
package com.feng.example;public class MyThreadA extends Thread {	private Thread b;		public MyThreadA(Thread b)	{		this.b = b;	}		@Override	public void run() {		try {			synchronized(b)			{//只是为了能够和线程b同步				System.out.println("begin A thread, ThreadName="+Thread.currentThread().getName()+"==="+System.currentTimeMillis());				Thread.sleep(5000);				System.out.println("end A thread, ThreadName="+Thread.currentThread().getName()+"==="+System.currentTimeMillis());			}		} catch (InterruptedException e) {			// TODO Auto-generated catch block			e.printStackTrace();		}	}}

测试类如下:

package com.feng.example;public class ThreadTest {	/**	 * @param args	 */	public static void main(String[] args) {						try {			Thread b = new MyThreadB();			Thread a = new MyThreadA(b);						a.start();			b.start();			b.join(2000);						System.out.println("main end"+"==="+System.currentTimeMillis());		} catch (InterruptedException e) {			// TODO Auto-generated catch block			e.printStackTrace();		}				}}

这个案例的结果并不是唯一的。首先看第一种输出情况

begin A thread, ThreadName=Thread-1===1450080738885end A thread, ThreadName=Thread-1===1450080743886begin B thread, ThreadName=Thread-0===1450080743886end B thread, ThreadName=Thread-0===1450080748886main end===1450080748886

分析:线程a,b, b.join(2000),三者所执行的操作都是以线程实例b作为锁对象的,也就是说三者需要同步执行。一般来说main函数中的代码执行要比启动线程的代码执行要快。(这不是重点),运行结果解释如下:

(1)三者中b.join(2000)首先抢到b的锁,然后代码执行到wait(delay)释放锁对象(去看上面的join(long)源码)

(2)线程a,b争抢锁,线程a获得锁对象,执行synchronized(b){}语句块,执行过程中Thread.sleep(5000)不释放锁,直到执行完成

(3)线程b与b.join(2000)方法争抢锁,线程b抢到锁对象,执行run方法,直到执行完

(4)b.join(2000)获得锁,发现线程b已经销毁,join方法执行完毕。

(5)打印main end语句。

上述解释中join争抢锁可以是在a执行完之后,不影响输出结果。

再看第二种运行情况:

begin A thread, ThreadName=Thread-1===1450082014528end A thread, ThreadName=Thread-1===1450082019529main end===1450082019529begin B thread, ThreadName=Thread-0===1450082019529end B thread, ThreadName=Thread-0===1450082024530

解释如下:

(1)三者中b.join(2000)首先抢到b的锁,然后代码执行到wait(delay)释放锁对象(去看上面的join(long)源码)

(2)线程a,b争抢锁,线程a获得锁对象,执行synchronized(b){}语句块,执行过程中Thread.sleep(5000)不释放锁,直到执行完成

(3)线程b与b.join(2000)方法争抢锁,b.join抢到锁,发现已经超过2s,因此join方法执行完成,输出main end,释放锁

(4)线程b获得锁,执行run方法

还有一只输出可能,这种可能不好演示,直接给出结果:

begin A thread, ThreadName=Thread-1===1450082014528end A thread, ThreadName=Thread-1===1450082019529begin B thread, ThreadName=Thread-0===1450082019529main end===1450082019529end B thread, ThreadName=Thread-0===1450082024530

解释如下:

(1)三者中b.join(2000)首先抢到b的锁,然后代码执行到wait(delay)释放锁对象(去看上面的join(long)源码)

(2)线程a,b争抢锁,线程a获得锁对象,执行synchronized(b){}语句块,执行过程中Thread.sleep(5000)不释放锁,直到执行完成

(3)线程b与b.join(2000)方法争抢锁,b.join抢到锁,发现已经超过2s,因此join方法执行完成,释放锁

(4)线程b获得锁,在主线程的输出语句运行之前执行了线程b的run方法,这里main线程与b线程属于异步执行。

从上例中可以看出join与sleep的不同,调用sleep时只是单纯的阻塞,并且不会释放锁。上例同时解释了join方法后语句提前执行的情况。

重点理解join方法获取锁,执行到wait方法直接释放锁的问题。

3.存在的困惑

主线程调用b.join方法,在join方法中是是让线程b执行wait方法,明明是线程b等待,又如何达到让主线程等待的功能呢?

b.wait到底wait的是什么,线程b为什么还能执行????

自我解释:

首先b.wait(),b只是代表一个锁,等待的是执行b.wait()的线程。

join和wait一样,哪个线程执行的此操作,等待的就是哪个线程,和调用者没有关系,调用者只是一个锁对象而已。

转载于:https://my.oschina.net/u/2309504/blog/544220

你可能感兴趣的文章
谈谈 MyBatis 的插件化设计
查看>>
页面可视化搭建工具前生今世
查看>>
UX设计秘诀之注册表单设计,细节决定成败
查看>>
前端通过post访问后端,后端断点断不住(没有收到request请求)
查看>>
ajax的请求方式(get和post的区别)?
查看>>
深度解读Helm 3: 犹抱琵琶半遮面
查看>>
Gson字符串数组转成List对象(泛型、Kotlin)
查看>>
dataX的入门到入土
查看>>
T-Tree、T*-Tree的理解与简单内存数据库的实现
查看>>
AppStore审核总被拒,可能的原因都在这儿了!
查看>>
Pinbox 网络收藏夹使用指南
查看>>
建站指南和总结
查看>>
飘刃,速度碾压 Vue-CLI 的轻量级 Vue 项目构建工具
查看>>
分布式架构springmvc+springboot+springcloud+redis
查看>>
【Linux Mint】Linux Mint 19.1 Cinnamon 修改鼠标滚动方向为自然方向
查看>>
Python 基础
查看>>
手势解锁,就这么简单
查看>>
js单线程和异步
查看>>
组件化开发的思考
查看>>
Python最简编码规范
查看>>