前言
再java.util.concurrent
包下,存在有几个辅助操作工具类。如CountDownLatch 减法计数器
、CyclicBarrier 加法计数器
和Semaphore 信号量
。
刚听到这几个类,很懵,下面就来说说这三个工具类具体怎么来用。
CountDownLatch 减法计数器
给定初始大小容量,每次线程进行某项操作时调用,容量-1,
等待其中无数据时,继续执行其他操作。
看下面的栗子:
import java.util.concurrent.CountDownLatch;
/**
* 减法 计数器;用于 必须保证所有线程都执行完后继续其他操作。
*/
public class CountDownTest {
public static void main(String[] args) throws InterruptedException {
// 1、计数器对象的创建,并给定计数器初始大小数
CountDownLatch countDownLatch = new CountDownLatch(6);
// 开启多个线程执行操作
for (int i = 1; i <= 6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" get ticket");
// 2、每个线程执行某项操作时,将计数器-1
countDownLatch.countDown();
},String.valueOf(i)).start();
}
// 3、等待计数器中的数据门票消耗完
countDownLatch.await();
// 4、打印信息
System.out.println("ticket num 0");
}
}
执行结果如下所示:
[发现:]
使用
await()
,让其他线程执行完成后,会继续等待,有点类似thread 中的 join()
,等待所有线程全部执行完成才会继续下面的其他操作。
查看jdk 1.8 开发手册,其中对方法有以下解释:
CyclicBarrier 加法计数器
上面说到了减法计数器
,所以必然也会有加法计数器
。其操作类为:CyclicBarrier
。
看下面的例子:
package demo5_1;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* 加法计数器
*/
public class CyclicBarrierDemo {
public static void main(String[] args) {
// 创建 10个 大小的 计数器
//CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
CyclicBarrier cyclicBarrier = new CyclicBarrier(10,()->{
System.out.println("票售完");
});
// 创建多线程
for (int i = 1; i <= 10 ; i++) {
new Thread(()->{
// 打印信息
System.out.println(Thread.currentThread().getName()+" get ticket");
// 计数器等待
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
运行后日志打印信息如下所示:
[发现:]
1、开始定义了一个加法计数器,采取的构造方式为
public CyclicBarrier(int parties, Runnable barrierAction)
。
2、每个线程执行某些业务后,调用cyclicBarrier.await()
,相当于添加至监控列表中。
3、当设定的容量被占用完,则会触发CyclicBarrier(int parties, Runnable barrierAction)
中的Runnable
。
查看源码await()
的执行逻辑:
Semaphore 信号量
信号量工具类,一般用于限定的资源类,
多线程处理时,如果有限定数量的线程全额占用,此时只允许未占用的线程执行等待;
当最初占用的线程释放占用后,后续线程才能获取使用。
类似于下面的逻辑:
和
令牌桶
算法逻辑很像哦。比如请求限流!
看图流程依旧还是很抽象,看下面的栗子:
package demo5_1;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* 信号量:用于某资源占用,其他处理等待其他线程处理完后才能处理
*/
public class SemphoreDemo {
public static void main(String[] args) {
// 假设 座位只有3个
Semaphore semaphore = new Semaphore(3);
// 创建线程
for (int i = 1; i <= 12 ; i++) {
new Thread(()->{
try {
// 获取座位(此处会造成阻塞 必须等待有空位才会分配,即向下执行)
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+" 获取座位");
// 假设拿到座位后占用2秒
TimeUnit.SECONDS.sleep(2);
// 离开座位 原则上应该放在finally中执行 --- 释放资源操作
//semaphore.release();
//System.err.println(Thread.currentThread().getName()+" 离开座位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
// 离开座位 原则上应该放在finally中执行--- 释放资源操作
semaphore.release();
// 用err只是颜色区分日志打印
System.err.println(Thread.currentThread().getName()+" 离开座位");
}
},String.valueOf(i)).start();
}
}
}
执行后的控制台输出日志如下: