CountDownLatch
countDownLatch主要有两个方法,countdown()与await()
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
//计数器是3
CountDownLatch countDownLatch = new CountDownLatch(3);
new Thread(()->{
countDownLatch.countDown(); //倒计时减一
}).start();
new Thread(()->{
countDownLatch.countDown(); //递减
}).start();
new Thread(()->{
countDownLatch.countDown();
}).start();
countDownLatch.await(); //阻塞 主线程 直到倒计时为0 的时候才会释放
System.out.println("执行完毕");
}
}
CountDownLatch执行流程
如果计数器不为0,main线程就会阻塞。CountDownlatch 用到的是共享锁。当计数器的值为0时会唤醒main线程,让main线程继续运行。否则阻塞main线程。
CyclicBarrier
1、 CyclicBarrier 是什么?
从字面上的意思可以知道,这个类的中文意思是“循环栅栏”。大概的意思就是一个可循环利用的屏障。 它的作用就是会让所有线程都等待完成后才会继续下一步行动。
2. 怎么使用 CyclicBarrier
2.1构造方法
public CyclicBarrier(int parties)
public CyclicBarrier(int parties, Runnable barrierAction)
解析:
- parties 是参与线程的个数
- 第二个构造方法有一个 Runnable 参数,这个参数的意思是最后一个到达线程要做的任务
2.2 重要方法
public int await() throws InterruptedException, BrokenBarrierException
public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException
解析:
- 线程调用 await() 表示自己已经到达栅栏
- BrokenBarrierException 表示栅栏已经被破坏,破坏的原因可能是其中一个线程 await() 时被中断或者超时
2.3 基本使用
2.3.1需求
一个线程组的线程需要等待所有线程完成任务后再继续执行下一次任务
2.3.2 代码实现
public class CyclicBarrierDemo {
static class TaskThread extends Thread {
CyclicBarrier barrier;
public TaskThread(CyclicBarrier barrier) {
this.barrier = barrier;
}
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println(getName() + " 到达栅栏 A");
barrier.await();
System.out.println(getName() + " 冲破栅栏 A");
Thread.sleep(2000);
System.out.println(getName() + " 到达栅栏 B");
barrier.await();
System.out.println(getName() + " 冲破栅栏 B");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
int threadNum = 5;
CyclicBarrier barrier = new CyclicBarrier(threadNum, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 完成最后任务");
}
});
for(int i = 0; i < threadNum; i++) {
new TaskThread(barrier).start();
}
}
}
打印结果:
Thread-1 到达栅栏 A
Thread-3 到达栅栏 A
Thread-0 到达栅栏 A
Thread-4 到达栅栏 A
Thread-2 到达栅栏 A
Thread-2 完成最后任务
Thread-2 冲破栅栏 A
Thread-1 冲破栅栏 A
Thread-3 冲破栅栏 A
Thread-4 冲破栅栏 A
Thread-0 冲破栅栏 A
Thread-4 到达栅栏 B
Thread-0 到达栅栏 B
Thread-3 到达栅栏 B
Thread-2 到达栅栏 B
Thread-1 到达栅栏 B
Thread-1 完成最后任务
Thread-1 冲破栅栏 B
Thread-0 冲破栅栏 B
Thread-4 冲破栅栏 B
Thread-2 冲破栅栏 B
Thread-3 冲破栅栏 B
从打印结果可以看出,所有线程会等待全部线程到达栅栏之后才会继续执行,并且最后到达的线程会完成 Runnable 的任务。
3. CyclicBarrier 使用场景
可以用于多线程计算数据,最后合并计算结果的场景。
4. CyclicBarrier 与 CountDownLatch 区别
- CountDownLatch 是一次性的,CyclicBarrier 是可循环利用的
- CountdownLatch阻塞主线程,等所有子线程完结了再继续下去,CyclicBarrier阻塞一组线程,直至某个状态之后再全部同时执行。
Semaphore
semaphore是信号量,表示一个许可。
public class SemaphoreDemo {
public static void main(String[] args) {
//令牌 许可 一次许可5个线程
Semaphore semaphore = new Semaphore(5);
for (int i = 0; i <10 ; i++) {
new DoAnything(i,semaphore).start();
}
}
static class DoAnything extends Thread{
private int num;
private Semaphore semaphore;
public DoAnything(int num, Semaphore semaphore) {
this.num = num;
this.semaphore = semaphore;
}
@Override
public void run(){
try {
semaphore.acquire(); //获取一个令牌
System.out.println("第" + num + "个线程进入");
Thread.sleep(2000); //todo 模拟业务代码
semaphore.release(); //释放令牌 将令牌的归还到桶里
System.out.println("第" + num + "个线程释放令牌");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
应用场景主要是限流,比如某个接口的吞吐量有限,为了保护当前接口的稳定性,就会限制接口的访问量。acquire让线程挂起,也用到了AQS。AQS是一个模板,多线程实现的核心,juc中大部分类的实现都用到了AQS。
AtomicInteger
保证原子性的操作,在多个线程操作共享数据时,他的原子递增。juc包中共有 12个元子操作,包括基本类型,数组类型,引用类型,属性类型即 字段类型。
原子类的原理 不停的做while循环获取数据,使用CAS去获取数据
原子类就是基于CAS操作实现的,CAS可能会产生ABA的问题。