1 join方法介绍
当前线程等待子线程死亡,才会执行。与sleep一样它也是一个可中断的方法,也就是说,如果有其他线程执行了对当前线程的interrupt操作,它也会捕获到中断信号,并且擦除线程的interrupt标识,Thread的API为我们提供了三个不同的join方法,具体如下:
public final void join() throws InterruptedException
public final synchronized void join(long millis,int nanos) throws InterruptedException
public final synchronized void join(long millis) throws InterruptedException
什么叫当前线程等待子线程死亡,才会执行?运行下面的代码
public static void main(String[] args) throws InterruptedException {
// 创建一个线程,打印1 到1000
Thread thread = new Thread(() -> {
try {
IntStream.range(1, 1000).forEach(i -> System.out.println(Thread.currentThread().getName() + " ==> " + i));
} catch (Exception e) {
e.printStackTrace();
}
},"MyThread");
thread.start();
// thread.join();
// 主线程进行同样的操作
IntStream.range(1, 1000).forEach(i -> System.out.println(Thread.currentThread().getName() + " ==> " + i));
}
会发现输出是交替输出的:
main ==> 252
main ==> 253
main ==> 254
main ==> 255
MyThread ==> 98
MyThread ==> 99
MyThread ==> 100
MyThread ==> 101
MyThread ==> 102
MyThread ==> 103
MyThread ==> 104
main ==> 256
main ==> 257
main ==> 258
打开注释掉的代码:
thread.join();
发现不是就不是交替输出,而是MyThread先执行完。
MyThread ==> 997
MyThread ==> 998
MyThread ==> 999
main ==> 1
main ==> 2
main ==> 3
main ==> 4
main ==> 5
join方法会使当前线程永远地等待下去,直到期间被另外的线程中断,或者join的线程执行结束,当然你也可以使用join的另外两个重载方法,指定毫秒数,在指定的时间到达之后,当前线程也会退出阻塞
注意:是等待子线程结束,比如这里是在主线程中创建的MyThead线程,所以主线程会等待MyThead执行结束。
在主线程中创建两个这样的线程,并且调用join
public static void main(String[] args) throws InterruptedException {
// 创建一个线程,打印1 到1000
Thread thread1 = new Thread(() -> {
try {
IntStream.range(1, 10000).forEach(i -> System.out.println(Thread.currentThread().getName() + " ==> " + i));
} catch (Exception e) {
e.printStackTrace();
}
},"MyThread1");
Thread thread2 = new Thread(() -> {
try {
IntStream.range(1, 10000).forEach(i -> System.out.println(Thread.currentThread().getName() + " ==> " + i));
} catch (Exception e) {
e.printStackTrace();
}
},"MyThread2");
thread1.start();
thread2.start();
thread1.join();
thread2.join();
// 主线程进行同样的操作
IntStream.range(1, 3).forEach(i -> System.out.println(Thread.currentThread().getName() + " ==> " + i));
}
- 主线程的开辟的这两个子线程还是交替随机打印
MyThread1 ==> 3227
MyThread1 ==> 3228
MyThread1 ==> 3229
MyThread1 ==> 3230
MyThread1 ==> 3231
MyThread2 ==> 3042
MyThread2 ==> 3043
MyThread2 ==> 3044
MyThread2 ==> 3045
MyThread2 ==> 3046
- 主线程会等到这两个子线程执行结束,才会执行自己的打印任务
2 join的简单应用示例
我们需要采集三个机器的数据,采集完三个机器的数据之后,统一进行保存准备使用三个线程分别对三个机器进行采集
该例子是典型的串行任务局部并行化处理。
- 根据要采集的数据定义一个数据模型,这里就简单声明
public class Data {
private String data;
public Data(String data) {
this.data = data;
}
public String getData() {
return data;
}
@Override
public String toString() {
return "Data{" +
"data='" + data + '\'' +
'}';
}
}
- 定义一个接口CaptureData,定义采集数据的规范
public interface CaptureData {
Data captureData();
}
- 定义一个采集任务,实现Runnable和CaptureData接口
// 定义采集的任务
@Slf4j
public class CaptureDataTask implements Runnable, CaptureData {
// 机器的名字
private final String machineName;
// 这次任务花费的时间,每个机器采集的时间可能不同,
private Long spendTime;
// 采集的结果
private Data result;
// 这里模拟每个采集的时间不同,在构造中也加入spendTime
public CaptureDataTask(String machineName, Long spendTime) {
this.machineName = machineName;
this.spendTime = spendTime;
}
@Override
public void run() {
try {
// 模拟采集数据的过程
log.info("[{}]{}开始采集数据", System.currentTimeMillis(), machineName);
result = captureData();
Thread.sleep(spendTime);
log.info("[{}]{}采集数据结束", System.currentTimeMillis(), machineName);
} catch (Exception e) {
e.printStackTrace();
}
}
// 返回采集的结果
public Data getResult() {
return result;
}
@Override
public Data captureData() {
// 模拟
return new Data(machineName + "的数据");
}
}
tips: 不管是Thread的run方法,还是Runnable接口,都是void返回类型,如果你想通过某个线程的运行得到结果,就需要自己定义一个返回的接口。
- 没有使用join
public static void main(String[] args) {
long startTimeStamp = System.currentTimeMillis();
Thread thread1 = new Thread(new CaptureDataTask("机器1", 10000L));
Thread thread2 = new Thread(new CaptureDataTask("机器2", 20000L));
Thread thread3 = new Thread(new CaptureDataTask("机器3", 30000L));
thread1.start();
thread2.start();
thread3.start();
long endTimeStamp = System.currentTimeMillis();
log.info("save data begin at [{}], end at [{}]", startTimeStamp, endTimeStamp);
}
- 测试:使用join
@Slf4j
public static void main(String[] args) throws InterruptedException {
CaptureDataTask task1 = new CaptureDataTask("机器1", 10000L);
Thread thread1 = new Thread(task1);
CaptureDataTask task2 = new CaptureDataTask("机器2", 20000L);
Thread thread2 = new Thread(task2);
CaptureDataTask task3 = new CaptureDataTask("机器3", 30000L);
Thread thread3 = new Thread(task3);
thread1.start();
thread2.start();
thread3.start();
thread1.join();
thread2.join();
thread3.join();
Data result1 = task1.getResult();
Data result2 = task2.getResult();
Data result3 = task3.getResult();
log.info("开始保存数据:[{}],[{}],[{}]", result1, result2, result3);
}