多线程CountDownLatch和Join

时间:2022-11-16 05:41:47

如果现在有五个线程A、B、C、D、E,请问如何用E线程用于统计A、B、C、D四个线程的结果?

题意需要用E线程统计A、B、C、D四个线程,也就是说E线程必须要等到前面四个线程运行结束之后才能执行。那么如何使用E线程来统计前面四个线程的结果呢?

下面介绍两种实现方法:

一、CountDownLatch

CountDownLatch是一种java.util.concurrent包下一个同步工具类,它允许一个或多个线程等待直到在其他线程中一组操作执行完成。

我们可以将CountDownLatch看作一个计数器,可以给定其一个数来对其进行初始化。当它计数的值变为0时,才可以执行后续的步骤。

public CountDownLatch(int count)

CountDownLatch有两个方法是我们常用的:await();和countDown();

await()函数用于阻塞当前线程直到CountDownLatch的计数值变为0;

countDown()方法用于将当前CountDownLatch的计数值减1;

简单的介绍了CountDownLatch的作用和用法之后,我们看下如下的代码:

import java.util.concurrent.CountDownLatch;
public class Test1{
public static void main(String[] args) throws InterruptedException {
int number = 3;
CountDownLatch latch = new CountDownLatch(number); for(int i=0;i<3;i++){
ThreadDemo demo = new ThreadDemo(latch);
demo.start();
System.out.println(i);
}
latch.await();
System.out.println("Check it Out");
}
} class ThreadDemo extends Thread{ private CountDownLatch latch; public ThreadDemo(CountDownLatch latch){
this.latch = latch;
} public void run(){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
}
}

我们首先创建了一个CountDownLatch对象给定一个number将其初始化,需要注意的是当前CountDownLatch对象的值减为0之后,该对象便失去了作用。

同时我们提供了继承自Thread的ThreadDemo类,它包含了一个CountDownLatch对象。当我们创建ThreadDemo对象时,会将CountDowmLatch对象注入其中。

在TreadDemo类的run()方法中,我们执行了两步动作:延迟5秒后将CountDownLatch的计数值减1。

在main函数中,我们利用for循环创建了三个ThreadDemo对象并执行他们的run()方法。添加和不添加latch.await()语句将会得到两种执行结果。

1、添加latch.await()语句

  执行结果:

  0

  1

  2

  Check it Out

  我们会发现0 1 2几乎是同时打印出来的,但是“Check it Out”大概等待了5秒左右的时间后才打印,这说明了其实“Check it Out”是等待前面三个线程执行完countDown()方法之后将latch的值减为0之后才执行的。

2、无latch.await()语句

  执行结果:

  0

  1

  2

  Check it Out

  观察运行结果我们会发现,0 1 2和“Check it Out”几乎是同时出现的,也就是说“Check it Out”并没有等待前面三个线程执行完成之后才执行。

  CountDowLlatch的用途即是让一组线程先执行,知道countdownlatch对象的计数值到0后,让后续的线程继续执行。

二、join方法实现

  事实上,join方法也可以实现同样的效果!代码如下:

import java.util.concurrent.CountDownLatch;
public class Test{
public static void main(String[] args) throws InterruptedException {
int number = 3;
CountDownLatch latch = new CountDownLatch(number);
ThreadDemo thread1 = new ThreadDemo(1);
ThreadDemo thread2 = new ThreadDemo(2);
thread1.start();
thread1.join();
System.out.println("----");
thread2.start();
thread2.join();
System.out.println("main is end!"); }
} class ThreadDemo extends Thread{
private int number;
public ThreadDemo(int number){
this.number = number;
}
public void run(){
System.out.println("Thread"+number+" is running");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread"+number+"is end");
}
}

运行结果如下:


Thread1 is running

Thread1is end

----

Thread2 is running

Thread2is end

main is end!

可以看到thread1阻塞了thread2,只有当thread1和thread2均执行结束后,main方法才能继续执行。如果想要thread1和thread2同时执行的话,只需要做如下简单的变动。

thread1.start();
thread1.join();
thread2.start();
thread2.join(); System.out.println("main is end!");

运行结果如下:

Thread1 is running

Thread2 is running

Thread1is end

Thread2is end

main is end!

因此,如果我们需要某些线程等待指定任务执行完毕之后执行,选择join方法也是一种选择;