文章目录
CountDownLatch
就是一个计数器
- await阻塞线程
- countDown计数器减1,计数器为0时唤醒await线程
让一个线程阻塞用法
public class CountDownExample {
static CountDownLatch countDownLatch=new CountDownLatch(3);
static class Thread1 extends Thread{
@Override
public void run(){
System.out.println("1干完了");
//表示我已经干完了
countDownLatch.countDown();
}
}
static class Thread2 extends Thread{
@Override
public void run(){
System.out.println("2干完了");
//表示我已经干完了
countDownLatch.countDown();
}
}
static class Thread3 extends Thread{
@Override public void run(){
System.out.println("3干完了");
//表示我已经干完了
countDownLatch.countDown();
}
}
public static void main(String[] args) throws InterruptedException {
//Thread.join()
Thread1 t1 = new Thread1();
t1.start();
Thread2 t2 = new Thread2();
t2.start();
Thread3 t3 = new Thread3();
t3.start();
//阻塞main线程
countDownLatch.await();
//被唤醒后打印
System.out.println("所有线程执行结束");
}
}
类似于Thread.join,但还是有比较大的差别。
让多个线程阻塞用法
public class CountDownExample {
static CountDownLatch countDownLatch=new CountDownLatch(1);
static class Thread1 extends Thread{
@Override
public void run(){
try {
countDownLatch.await();
System.out.println("主线程跑完了,1可以跑了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class Thread2 extends Thread{
@Override
public void run(){
try {
countDownLatch.await();
System.out.println("主线程跑完了,2可以跑了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class Thread3 extends Thread{
@Override
public void run(){
try {
countDownLatch.await();
System.out.println("主线程跑完了,3可以跑了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
//相似于Thread.join
Thread1 t1=new Thread1();
t1.start();
Thread2 t2=new Thread2();
t2.start();
Thread3 t3=new Thread3();
t3.start();
Thread.sleep(1000);
System.out.println("睡眠晚了");
countDownLatch.countDown();
}
}
实际应用(检测数据库,缓存两个服务的状态)
检查类
public class ApplicationStartup {
private static List<BaseHealthChecker> services;
private static CountDownLatch countDownLatch=new CountDownLatch(2);
static{
services=new ArrayList<>();
services.add(new CacheHealthChecker(countDownLatch));
services.add(new DatabaseHealthChecker(countDownLatch));
}
private final static ApplicationStartup INSTANCE=new ApplicationStartup();
private ApplicationStartup(){}
public static ApplicationStartup getInstance(){
return INSTANCE;
}
public static boolean checkExternalServices() throws InterruptedException {
for(BaseHealthChecker bh:services){
new Thread(bh).start(); //针对每个服务采用线程来执行
}
countDownLatch.await();
return true;
}
}
public abstract class BaseHealthChecker implements Runnable{
private String serviceName; //服务名称
private boolean serviceUp;
public BaseHealthChecker(String serviceName) {
this.serviceName = serviceName;
}
@Override
public void run() {
try {
verifyService();
serviceUp=true;
}catch (Exception e){
serviceUp=false;
}finally {
}
}
/**
* 检查服务的健康情况
*/
public abstract void verifyService() throws Exception;
public String getServiceName() {
return serviceName;
}
public boolean isServiceUp() {
return serviceUp;
}
}
数据库检查类
public class DatabaseHealthChecker extends BaseHealthChecker{
private CountDownLatch countDownLatch;
public DatabaseHealthChecker(CountDownLatch countDownLatch) {
super("DatabaseHealthChecker");
this.countDownLatch=countDownLatch;
}
@Override
public void verifyService() throws Exception {
System.out.println("Checking:"+this.getServiceName());
try {
Thread.sleep(1000);
} catch (Exception e) {
throw e;
}
countDownLatch.countDown();
System.out.println(this.getServiceName()+" 健康状态正常");
}
}
缓存检查类
public class CacheHealthChecker extends BaseHealthChecker{
private CountDownLatch countDownLatch;
public CacheHealthChecker(CountDownLatch countDownLatch) {
super("CacheHealthChecker");
this.countDownLatch=countDownLatch;
}
@Override
public void verifyService() throws Exception {
System.out.println("Checking:"+this.getServiceName());
try {
Thread.sleep(1000);
// 如果检查失败,throw RuntimeException()
} catch (Exception e) {
throw e;
}
countDownLatch.countDown();
System.out.println(this.getServiceName()+" 健康状态正常");
}
}
启动类
public class StartupMain {
public static void main(String[] args) {
try {
ApplicationStartup.checkExternalServices();
} catch (InterruptedException e) {
//有问题了.
}
System.out.println("服务启动成功");
}
}
这样就变多线程了,不用顺序检查!
思考底层原理
让多个线程阻塞其实就是共享锁的实现。
即:可以允许多个线程同时抢占到锁,然后等到计数器归零的时候,同时唤醒.
- state作为记录计数器.
- countDown的时候,实际上就是 state–
- 需要一个数据结构存储被阻塞的线程
再看源码
跟AQS果然关联起来了
再看await方法
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)//返回-1说明state!=0,可以抢占共享锁
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;//state!=0返回-1,==0返回1
}
Semaphore
信号灯.
限流器,限制资源的访问,限制同时执行的线程数量
本质上: 抢占一个令牌,如果抢占到令牌,就通行, 否则,就阻塞!
- acquire() 抢占一个令牌,可以传递参数,表示一次抢占多少个令牌
- release() 释放一个令牌.
经典案例
public class SemaphoreExample {
public static void main(String[] args) {
//限制资源的并发数量.
Semaphore semaphore=new Semaphore(10);
for (int i = 0; i < 20; i++) {
new Car(i,semaphore).start();
}
}
static class Car extends Thread{
private int num;
private Semaphore semaphore;
public Car(int num, Semaphore semaphore) {
this.num = num;
this.semaphore = semaphore;
}
@Override
public void run(){
try {
semaphore.acquire(); //获得一个令牌
System.out.println("第 "+num+"俩车抢到一个车位");
TimeUnit.SECONDS.sleep(2);
System.out.println("第 "+num+"走喽~");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release(); //释放一个令牌
}
}
}
}
CyclicBarrier
可重复的栅栏
public class CyclicBarrierExample {
public static void main(String[] args) {
int n=4;
//参数4代表参与线程有4个,后面就是这4个线程都执行完了,即调用wait减到0了,再执行这个后续的东西
CyclicBarrier barrier=new CyclicBarrier(4,()->{
System.out.println("所有线程都写入完成,继续处理其他任务");
}); // 4
for (int i = 0; i < n; i++) {
new Writer(barrier).start();
}
}
static class Writer extends Thread{
private CyclicBarrier cyclicBarrier;
public Writer(CyclicBarrier barrier){
this.cyclicBarrier=barrier;
}
@Override
public void run(){
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"写入数据完毕,等待其他线程");
cyclicBarrier.await(); //-1的动作
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
Exchanger
不常用