1.CountdownLatch
用来进行线程同步协作,等待所有线程完成倒计时。
其中构造参数用来初始化等待计数值,await() 用来等待计数归零, countDown( 用来让计数减一
什么时候倒计时减为0,等待的线程就可以运行了
代码:
package demo1;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchTest {
/*让主线程等待其他线程结束后在运行*/
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch=new CountDownLatch(3);
new Thread(()->{
System.out.println(Thread.currentThread()+"waiting");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();//将计数器减一
System.out.println(Thread.currentThread()+"end");
}).start();
new Thread(()->{
System.out.println(Thread.currentThread()+"waiting");
try {
Thread.sleep(150);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();//将计数器减一
System.out.println(Thread.currentThread()+"end");
}).start();
new Thread(()->{
System.out.println(Thread.currentThread()+"waiting");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();//将计数器减一
System.out.println(Thread.currentThread()+"end");
}).start();
System.out.println(Thread.currentThread()+"waiting");
countDownLatch.await();//等待计数器减为0
System.out.println(Thread.currentThread()+"wait end");
}
}
也可以使用join来实现让其他线程先运行,最后让主线程运行。有什么区别吗?
join是属于比较底层的api用起来比较繁琐,而CountDownLatch是属于高级的api,使用得场景比较多
例如用线程池去创建线程,线程是不会结束的,所以就不能用join等待线程结束再运行主线程的方法
使用线程池改进上述代码:
前三个线程去干活,最后一个线程在最后做总结
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountDownLatchTest {
/*让主线程等待其他线程结束后在运行*/
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch=new CountDownLatch(3);
ExecutorService executors=Executors.newFixedThreadPool(4);
executors.submit(()->{
System.out.println(Thread.currentThread()+"waiting");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();//将计数器减一
System.out.println(Thread.currentThread()+"end"+countDownLatch.getCount());
});
executors.submit(()->{
System.out.println(Thread.currentThread()+"waiting");
try {
Thread.sleep(150);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();//将计数器减一
System.out.println(Thread.currentThread()+"end"+countDownLatch.getCount());
});
executors.submit(()->{
System.out.println(Thread.currentThread()+"waiting");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();//将计数器减一
System.out.println(Thread.currentThread()+"end"+countDownLatch.getCount());
});
executors.submit(()->{
try {
System.out.println(Thread.currentThread()+"waiting");
countDownLatch.await();//等待计数器减为0
System.out.println(Thread.currentThread()+"wait end");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
应用—等待多线程执行完毕
游戏等待玩家加载,玩家都加载完成后,游戏开始
知识点:
1.\r:回车,后一次打印的结果会覆盖上一个结果
2.lambd表达式只能引用常量不能引用变量(定义一个局部常量将变量赋值给他)
代码:
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountDownLatchTest2 {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(10);
String[] s = new String[10];
CountDownLatch latch=new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
int k = i;
service.submit(() -> {
for (int j = 0; j <= 100; j++) {
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
s[k] = j + "%";
System.out.print("\r"+ Arrays.toString(s));
}
latch.countDown();//计数器减一
});
}
latch.await();//等待计数器减为0
System.out.println("\n游戏开始");
service.shutdown();
}
}
应用—同步等待多个远程调用结束
怎么让线程池中的信息返回给主线程?
需要配合Future
用future来实现线程的返回值
因为CountdownLatch每次使用都要重新创建一个对象,不能复用,因为它没有set方法。这个时候引入了CyclicBarrier
2.CyclicBarrier
[ saiklik 'baeria]
循环栅栏,用来进行线程协作,等待线程满足某个计数。构造时设置[计数个数」,每个线程执行到某个需
要“同步’的时刻调用await()方法进行等待,当等待的线程数满足[「计数个数」时,继续执行
它的CountdownLatch一样都是计数器,但是它是可以重用的,可以恢复到计数器最初的数值
构造方法的第二个参数是等其他的线程将加速器减为0了,它才可以运行的一个线程任务
代码:
package demo1;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CyclicBarrierTest {
public static void main(String[] args) {
ExecutorService service= Executors.newFixedThreadPool(2);
CyclicBarrier barrier=new CyclicBarrier(2,()->{
System.out.println("task1,task2 finsh..");
});
for (int i = 0; i <3 ; i++) {
service.submit(()->{
System.out.println("task1 begin");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
barrier.await();//计数器减少1
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
});
service.submit(()->{
System.out.println("task2 begin");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
barrier.await();//计数器减少1
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
});
}
service.shutdown();
}
}
注意:
在上述代码中 线程数要和CyclicBarrier计数要一样。
为什么呢?如果线程池数为3,计数器数为2,两个任务去执行,任务一执行1s,任务二执行三秒,那么最后两次任务一就把计数器数减为了0.