CountDownLatch
CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器功能
eg:如有一个任务A,要等待其他n个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现,每当线程完成任务后,计数器值就会减1.当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。
使用方法初始化的时候给定一个初始化的值
每个线程完成后调用countDown()方法,计数到达0,则释放所有等待
await()如果不是0,则使当前线程在锁存器倒计至零之前一直等待,除非线程被中断,等到0之后才唤醒主线程
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class FightQueryDemo {
private static List<String> company= Arrays.asList("东方航空","南方航空","海南航空");
private static List<String> fightList=new ArrayList<>();
public static void main(String[] args) throws InterruptedException{
String origin="BJ";
String dest="SH";
Thread[] threads=new Thread[company.size()];
CountDownLatch latch=new CountDownLatch(company.size());
for (int i = 0; i < threads.length; i++) {
String name=company.get(i);
threads[i]=new Thread(()->{
System.out.printf("%s 查询从%s到%s的机票\n",name,origin,dest);
//随机产生票数
int val=new Random().nextInt(10);
try {
TimeUnit.SECONDS.sleep(val);
fightList.add(name+"--"+val);
System.out.printf("%s公司查询成功!\n",name);
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
threads[i].start();
}
latch.await();
System.out.println("==============查询结果如下:================");
fightList.forEach(System.out::println);
}
}
前半部分是串行 中间查询是并行 最后又回到主线程执行
CyclieBarrier
cyclieBarrier意思是回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier 可以被重用。我们暂且把这个状态叫做 barrier,当调用 await()方法之后,线程就处于 barrier了
基本原理:每个线程执行时,都会碰到一个屏障,直到所有线程执行结束,然后屏障才会打开,使所有线程继续往下执行。
在CyclicBarrier 的内部定义了一个 Lock 对象,每当一个线程调用 await方法时,将拦截的线程数加1,然后判断剩余拦截数是否为初始值 parties,如果不是,进入Lock对象的条件队列等待。如果是,执行barrierAction 对象的 Runnable 方法,然后将锁的条件队列中的所有线程放入锁等待队列中,这些线程会依次的获取锁、释放锁。
CycliBarrier的两个构造函数:CyclicBarrier(int parties)和 CyclicBarrier(int parties,Runnable barrierAction) 前者只需要声明拦截的线程数即可,后者需要定义一个等待所有线程到达屏障优先执行的 Runnable对象
import java.util.Random;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
public class RaceDemo {
public static void main(String[] args) {
CyclicBarrier barrier=new CyclicBarrier(8);
Thread[] play=new Thread[8];
for (int i = 0; i < 8; i++) {
play[i]=new Thread(()->{
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
System.out.println(Thread.currentThread().getName()+"准备好了");
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("选手"+Thread.currentThread().getName()+"起跑");
},"play["+i+"]");
play[i].start();
}
}
}
CountDownLatch 和 CyclicBarrier 区别
对于CountDownLatch 和 CyclicBarrier 两个类,都是多个线程要做完事情之后等待其他线程完成,全部线程完成之后再恢复运行。不同的是 CountDownLatch 类需要你自己调用 countDown()方法减少一个计数器,然后调用 await() 方法即可。而 CyclicBarrier 则直接调用 await()方法即可
CountDownLatch -1
CyclicBarrier +1
CountDownLatch 更倾向于多个线程合作的情况,等你所有东西准备好了,在自动执行。而CyclicBarrier 则是我们都在一个地方等你,大家到齐了,大家再一起执行
Semaphore
Semaphore 计数信号量,维护一个许可集,初始化默认是非公平锁并需要传入需要创建的许可数,也
- 信号量可以使用的资源数量
- 步骤
- 请求资源 acquire ,获取许可
- 使用资源 (业务处理)
- 释放资源 release()
资源有限共享,如停车场 只有5个停车位
import com.sun.javafx.animation.TickCalculation;
import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class CarDemo {
public static void main(String[] args) {
//创建Semaphore
Semaphore sp=new Semaphore(5);
Thread[] car=new Thread[10];
for (int i = 0; i < 10; i++) {
car[i]=new Thread(()->{
//请求许可
try {
sp.acquire();
System.out.println(Thread.currentThread().getName()+"可以进停车场");
} catch (InterruptedException e) {
e.printStackTrace();
}
//使用资源
try {
int val= new Random().nextInt(10);
TimeUnit.SECONDS.sleep(val);
System.out.println(Thread.currentThread().getName()+"停留了"+val+"秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
//离开(释放资源)
try {
sp.release();
System.out.println(Thread.currentThread().getName()+"离开停车场");
} catch (Exception e) {
e.printStackTrace();
}
},"car["+i+"]");
car[i].start();
}
}
}