在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程将早于子线程结束。这时,如果主线程想等子线程执行完成才结束,比如子线程处理一个数据,主线程想要获得这个数据中的值,就要用到join()方法了。方法join()的作用是等待线程对象销毁。
- join方法介绍
join方法的主要作用就是同步,它可以使得线程之间的并行执行变为串行执行在A线程中调用了B线程的join()方法时,表示只有当B线程执行完毕时,A线程才能继续执行。
join方法中如果传入参数 表示:如果A线程中掉用B线程的join(10)
,则表示A线程会等待B线程执行10毫秒,10毫秒过后,A、B线程并行执行。需要注意的是,jdk规定,join(0)
的意思不是A线程等待B线程0秒,而是A线程等待B线程无限时间,直到B线程执行完毕,即join(0)
等价于join()
。(其实join()中调用的是join(0)
)
方法join(long)
的功能在内部是使用wait(long)
来实现的,所以join(long)
方法具有释放锁的特点。
还要注意的是:join方法必须在线程start方法调用之后调用才有意义。这个也很容易理解:如果一个线程都没有start,那它也就无法同步了。
join使用举例:
public class Demo1 extends Thread {
/**
* 更改线程名字
*/
public Demo1(String threadName) {
this.setName(threadName);
}
@Override
public void run() {
for (int i = 0; i < 2; i++) {
try {
Thread.sleep(1 * 500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
public static void main(String[] args) {
Demo1 t1 = new Demo1("t1");
Demo1 t2 = new Demo1("t2");
Demo1 t3 = new Demo1("t3");
t1.start();
/**
* join的意思是使得放弃当前线程的执行,
* 并返回对应的线程,例如下面代码的意思就是:
* 程序在main线程中调用t1线程的join方法,则main线程放弃cpu控制权,
* 并返回t1线程继续执行直到线程t1执行完毕
* 所以结果是t1线程执行完后,才到主线程执行,
* 相当于在main线程中同步t1线程,t1执行完了,main线程才有执行的机会
*/
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (t2.isAlive()) {
System.out.println("t2 is alive");
} else { // t2还没有start,所以上面if一定进不去,打印else
System.out.println("t2 is not alive");
}
t2.start();
t3.start();
}
}
结果:
t1-----0
t1-----1
t2 is not alive
t3-----0
t2-----0
t2-----1
t3-----1
-
方法x.join()的作用是使所属线程x 正常执行run()中的方法,而使得调用x.join()的线程处于无限期阻塞状态,等待x线程销毁后再继续执行线程z后面的代码。
-
方法join()具有使线程排队运行的作用,有些类似于同步的运行效果。join()与synchronized的区别是:join在内部调用wait()方法进行等待,而synchronized关键字使用的是"对象监视器"原理作为同步。
-join与异常
在join()过程中,如果当前线程被中断,则当前线程出现异常。(注意是调用thread.join()的线程被中断才会进入异常,比如a线程调用b.join(),a中断会报异常而b中断不会异常)
如下:threadB中启动threadA,并且调用其方法等待threadA完成,此时向threadB发出中断信号,会进入中断异常代码。
/**
* 线程类join()使用方法--join中中断
*/
public class Demo2 extends Thread {
private static final Logger LOGGER = LoggerFactory.getLogger(Demo2.class);
public static void main(String[] args) throws InterruptedException {
final Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
LOGGER.info("threadA run");
while (true) {
}
}
}, "threadA");
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
LOGGER.info("threadB run");
threadA.start();
try {
threadA.join();
} catch (InterruptedException e) {
LOGGER.error("join error ,threadName - > {}", Thread.currentThread().getName(), e);
}
}
}, "threadB");
threadB.start();
// 向threadB发出中断信号
Thread.sleep(1 * 1000);
threadB.interrupt();
}
}
上面虽然进入异常代码块,但是线程仍然未停止 (因为threadA并没有抛出异常,所以仍然在存活)
-join(long)与sleep(long)的区别
方法join(long)
的功能在内部是使用wait(long)
来实现的,所以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(long)方法之后内部调用了wait()方法,因此会释放该对象锁。
本文转载自:https://www.cnblogs.com/qlqwjy/p/10120457.html