10.1、CountDownLatch(计数器)
官方文档中,对这一块的解释比较复杂,简单来说,该类就是一个减法计数器
,一般用于计算线程的执行次数,当达到0时执行某一操作。
举个例子,假设现在一个班上有6名学生,现在放学了,老师要关教室门,就必须要等6名同学全部走完才能关门,因此,每走一个学生,就对剩下的学生总数-1,直到所有学生都走完,老师才能关教室门。
我们来实现这个案例:
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
//定义计数器,计数6次
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i < 7; i++) {
new Thread(()-> {
System.out.println(Thread.currentThread().getName()+"号学生出教室");
countDownLatch.countDown(); //每次走出一个学生,计数器-1
},String.valueOf(i)).start();
}
countDownLatch.await(); //当计数器归0时执行后面的程序
System.out.println("老师关门");
}
}
运行结果:
由此,我们就完成了这个案例,可以看出,CountDownLatch的作用就是帮我们记录线程的执行次数,每运行一条线程,调用了countDown() 方法,就进行一次-1计数,当计数为0时,就唤醒await()方法去执行后面的代码;不为0就会一直阻塞。
在CountDownLatch中,我们主要记住这两个方法即可:
- countDown(); 计数-1
- await(); 等待计数归0,然后再向下执行
10.2、CyclicBarrier
同样与CountDownLatch,官方对这一块的解释也十分抽象。其实简单来说,这就是一个加法计数器,毕竟计数有了减法,就一定有加法。
可以看到,在创建CyclicBarrier对象时,有两个构造的重载,第一个重载有两个参数分别代表的意思是int:记录线程的次数
,Runnable:达到次数后执行的线程
;
我们用一个案例来表示:
假设现在有7个人准备一起吃饭,而服务员要等7个人到齐了才会上菜
代码实现
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(7, () -> {
System.out.println("服务员上菜");
});
for (int i = 1; i <= 7; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"客人到了");
try {
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
运行结果
可以看到,每次运行一条线程,线程就会进行等待并+1,直到7条线程全部执行,就会唤醒我们在创建对象中传递的服务员线程进行执行;
10.3、Semaphore(信号量)
简单来讲,Semaphore就是为限制线程个数而生的;举个简单的例子,现在有6辆车,但是停车场只有3个车位,因此必定会有3辆车在外面等候有空余车位时才能停车。当其中的一辆或多辆车准备离开时,就释放了停车的空间,其他车就可以进来停车。
案例实现:
public class SemaphoreDemo {
public static void main(String[] args) {
//设置3个车位
Semaphore semaphore = new Semaphore(3);
//创建6条线程,模拟6辆车
for (int i = 1; i <= 6; i++) {
new Thread(()->{
try {
semaphore.acquire(); //占用资源
System.out.println(Thread.currentThread().getName()+"号车停车,占用车位");
TimeUnit.SECONDS.sleep(2); //设置延迟,模拟占用车位
System.out.println(Thread.currentThread().getName()+"号车已离开");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release(); //释放资源
}
},String.valueOf(i)).start();
}
}
}
运行结果
可以看到,使用Semaphore可以对线程数的控制;
- acquire(); 获取资源,该方法会阻塞,直到有资源可用,才能使用它
- release(); 释放资源。
使用该类进行线程控制,能够实现一种限流的操作,控制最大线程数,可以保证服务器硬件资源的安全。