juc包下的四大工具类
CountDownLatch
CyclicBarrier
Exchange
Semaphore
1.CountDownLatch–闭锁
作用为:解决一个线程需等待其他线程完成任务后再次恢复执行的情景。
注意:每个CountDownLatch对象的计数器在减为0时不可恢复原值。
CountDownLatch类常用方法:
//调用await()方法的线程阻塞直到其他线程完成任务才可恢复执行
1.public void await() throws InterruptedException {}
//await()方法的重载方法,作用为等待timeout时间后无论其他线程是否完成任务均恢复执行
2.public boolean await(long timeout, TimeUnit unit) throws InterruptedException {}
//CountDownLatch类唯一的构造方法,count为需要等待完成任务线程数量
3.public CountDownLatch(int count) {}
//该方法为兵法执行任务的线程调用,调用countDown方法相当于已执行完任务,count--,直到count=0时最初调用await()方法的阻塞线程才可恢复执行
4.public void countDown() {}
假想一个这样的场景:运动场上,裁判一声哨响,运动员起跑,裁判开始计时,等待最后一位运动员到达终点时裁判才可按下计时器,结束比赛。那么也就是说裁判要等到所有运动员都到达终点后才可按下计时器。那这个场景我们要怎么实现呢?使用之前多线程部分的知识,我们很容易就能想到join()方法,假想裁判是主线程,运动员均是子线程,使用join()方法就需要在所有子线程在主线程中调用join()方法,但线程在调用join()方法的先后顺序上也可能影响运行结果。
针对以上场景,我们可用CountDownLatch完美解决:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
class CountDownLatchTest implements Runnable{
private CountDownLatch countDownLatch;
public CountDownLatchTest(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"到达终点");
countDownLatch.countDown();
}
}
public class ThreadTest{
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch=new CountDownLatch(3);
System.out.println("比赛开始...");
new Thread(new CountDownLatchTest(countDownLatch),"运动员A").start();
TimeUnit.SECONDS.sleep(2);
new Thread(new CountDownLatchTest(countDownLatch),"运动员B").start();
TimeUnit.SECONDS.sleep(2);
new Thread(new CountDownLatchTest(countDownLatch),"运动员C").start();
countDownLatch.await();
System.out.println("所有运动员均到达终点,比赛结束!");
}
}
/*
比赛开始...
运动员A到达终点
运动员B到达终点
运动员C到达终点
所有运动员均到达终点,比赛结束!
*/
CountDownLatch图示:
2.CyclicBarrier–循环栅栏
注意:每个CyclicBarrier对象可重复使用。
CyclicBarrier常用方法:
//parties为需阻塞子线程个数
1.public CyclicBarrier(int parties) {}
//parties为需阻塞子线程个数,barrierAction为等所有线程到达同步点后,随机唤醒一个线程执行barrierAction任务,之后再全部线程恢复执行
2.public CyclicBarrier(int parties, Runnable barrierAction) {}
//子线程调用await()方法阻塞当前线程,CyclicBarrier计数器减一,直到减为0后恢复执行
3.public int await() throws InterruptedException, BrokenBarrierException {}
假想这样场景:有一本书,三个人分模块同时书写,假设A先写完,此时书不可能直接出版,必须等到剩余两人都写完才可以出版。下面用代码实现此场景:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
class CycliBarrierTest implements Runnable{
private CyclicBarrier cyclicBarrier;
public CycliBarrierTest(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"已到达栅栏");
try {
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName()+"已完成任务");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
public class ThreadTest{
public static void main(String[] args) throws InterruptedException {
CyclicBarrier cyclicBarrier=new CyclicBarrier(3);
for(int i=0;i<3;i++){
new Thread(new CycliBarrierTest(cyclicBarrier),"线程"+(i+1)).start();
TimeUnit.SECONDS.sleep(2);
}
}
}
/**
线程1已到达栅栏
线程2已到达栅栏
线程3已到达栅栏
线程3已完成任务
线程1已完成任务
线程2已完成任务
**/
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
class CycliBarrierTest implements Runnable{
private CyclicBarrier cyclicBarrier;
public CycliBarrierTest(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"已到达栅栏");
try {
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName()+"已完成任务");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
public class ThreadTest{
public static void main(String[] args) throws InterruptedException {
CyclicBarrier cyclicBarrier=new CyclicBarrier(3, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行barrierAction任务");
}
});
for(int i=0;i<3;i++){
new Thread(new CycliBarrierTest(cyclicBarrier),"线程"+(i+1)).start();
TimeUnit.SECONDS.sleep(2);
}
}
}
/**
线程1已到达栅栏
线程2已到达栅栏
线程3已到达栅栏
线程3执行barrierAction任务
线程1已完成任务
线程2已完成任务
线程3已完成任务
**/
CyclicBarrier图示:
3.Exchanger—线程数据交换器
应用场景:用于两个线程交换数据(不能用于多个)
常用方法:
public V exchange(V x) throws InterruptedException {}//调用exchange()方法会阻塞当前线程,必须等到另外一个线程时才可进行数据交换
Exchanger简单实现:
import java.util.concurrent.Exchanger;
public class ThreadTest{
public static void main(String[] args) {
Exchanger<String> exchanger=new Exchanger<>();
Thread girl=new Thread(new Runnable() {
@Override
public void run() {
try {
String str=exchanger.exchange("我喜欢你!");
System.out.println("女孩说:"+str);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
girl.start();
Thread boy=new Thread(new Runnable() {
@Override
public void run() {
try {
String str=exchanger.exchange("我也是!");
System.out.println("男孩说:"+str);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
boy.start();
}
}
/**
男孩说:我喜欢你!
女孩说:我也是!
**/
Exchange图示:
4.Semaphore—信号量
常用方法:
//调用acquire()方法默认为申请一个信号量
1.public void acquire() throws InterruptedException {}
//permits为申请信号量数
2.public void acquire(int permits) throws InterruptedException {}
//默认释放一个信号量
3.public void release() {}
//释放permits个信号量
4.public void release(int permits) {}
假想一个这样的场景:一个工厂有8个工人5台机器,每台机器最多只能一个人进行操作,因此最多5人工作。
以下为Semaphore实现以上情景:
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
class SemaphoreTest implements Runnable{
private Semaphore semaphore;
private Integer num;
public SemaphoreTest(Semaphore semaphore, Integer num) {
this.semaphore = semaphore;
this.num = num;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("工人"+this.num+"占用一台设备");
TimeUnit.SECONDS.sleep(2);
System.out.println("工人"+this.num+"释放本设备");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadTest{
public static void main(String[] args) {
Semaphore semaphore=new Semaphore(5);
//8个工人,5台机器
for(int i=0;i<8;i++){
new Thread(new SemaphoreTest(semaphore,i+1)).start();
}
}
}
/**
工人1占用一台设备
工人2占用一台设备
工人3占用一台设备
工人4占用一台设备
工人5占用一台设备
工人2释放本设备
工人4释放本设备
工人5释放本设备
工人3释放本设备
工人8占用一台设备
工人1释放本设备
工人7占用一台设备
工人6占用一台设备
工人8释放本设备
工人7释放本设备
工人6释放本设备
**/
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
class SemaphoreTest implements Runnable{
private Semaphore semaphore;
private Integer num;
public SemaphoreTest(Semaphore semaphore, Integer num) {
this.semaphore = semaphore;
this.num = num;
}
@Override
public void run() {
try {
semaphore.acquire(2);
System.out.println("工人"+this.num+"占用一台设备");
TimeUnit.SECONDS.sleep(2);
System.out.println("工人"+this.num+"释放本设备");
semaphore.release(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadTest{
public static void main(String[] args) {
Semaphore semaphore=new Semaphore(5);
//8个工人,5台机器
for(int i=0;i<8;i++){
new Thread(new SemaphoreTest(semaphore,i+1)).start();
}
}
}
/**
工人2占用一台设备
工人3占用一台设备
工人2释放本设备
工人4占用一台设备
工人3释放本设备
工人1占用一台设备
工人1释放本设备
工人4释放本设备
工人5占用一台设备
工人6占用一台设备
工人6释放本设备
工人5释放本设备
工人7占用一台设备
工人8占用一台设备
工人8释放本设备
工人7释放本设备
**/
Semaphore图示: