Java并发编程9——CountDownLatch / CyclicBarrier / Semaphore

  • CountDownLatch 
    • 介绍与用法
    • 原理和Api
  • CyclicBarrier
    • 介绍与用法
    • 原理和Api
  • Semaphore
    • 介绍与用法
    • 原理和Api

1. CountDownLatch

(1)介绍

  • 也称 倒计时锁 / 闭锁,用来进行线程同步协作,让一个线程等待其他所有线程完成倒计时后再恢复运行
  • 可以理解为加强版join(),等待n个线程执行完,再恢复运行

(2)用法

  • 构造器: CountDownLatch latch = new CountDownLatch(n),意为等待n个线程执行完,再恢复运行 
  • 需要被恢复的线程执行await(),其他线程执行完后countDown()
    • 这里主线程调用:latch.await(),类似join()等待
    • 其他线程执行:latch.countDown()
public static void main(String[] args) throws InterruptedException {
        //创建
        CountDownLatch latch = new CountDownLatch(2);

        //执行thread0
        Thread thread0 = new Thread(()->{
            try {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread() + "   thread0 start...");
                //会让latch的计数器-1;
                latch.countDown();
                System.out.println(Thread.currentThread() +"   thread0 end...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        //执行thread1
        Thread thread1 = new Thread(()->{
            try {
                Thread.sleep(2000);
                System.out.println(Thread.currentThread() + "   thread1 start...");
                //会让latch的计数器-1;
                latch.countDown();
                System.out.println(Thread.currentThread() + "   thread1 end...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        thread0.start();
        thread1.start();
        System.out.println(Thread.currentThread() +"   main thread start...");
        //进入waiting一直等待到有2个线程结束后再恢复
        latch.await();
        System.out.println(Thread.currentThread() + "   main thread end...");
    }
  • 运行结果:主线程执行到 latch.await()后被挂起,直到有2个线程都执行到了latch.countDown()后,主线程继续进入RUNNING态

 

(3)原理

  • 静态内部类Sync继承AQS
    • 不同于ReentrantLock,该Sync为共享锁,state变量记录共享模式的个数(初始值为构造器参数count)
    • Sync内部重写了tryAcquireShare()和tryReleaseShared()方法
    • 每次子线程执行完毕调用countDownLatch.countDown()方法后让count-1,count=0后主线程才返回
private static final class Sync extends AbstractQueuedSynchronizer {
    Sync(int count) {
            setState(count);
        }

    int getCount() {
        return getState();
    }

    protected int tryAcquireShared(int acquires) {
        return (getState() == 0) ? 1 : -1;
    }

    protected boolean tryReleaseShared(int releases) {
        for (;;) {
            int c = getState();
            if (c == 0)    return false;              
            int nextc = c-1;
            if (compareAndSetState(c, nextc))
                    return nextc == 0;
        }
    }
}

(4)Api

void await()

委托Sync调用AQS的doAcquireShareInterruptibly()方法阻塞当前线程;

实际还是调用自己重写的tryAcquireShared()方法

两种情况会恢复运行态:

(1)其他线程调用countDown()方法,使得计数器为0

(2)其他线程调用interrupt(),该线程被打断,抛出InterruptedException异常

boolean await(long, TimeUnit)

委托Sync调用AQS的doAcquireShareNanos(1, unit.toNanos(timeout))带超时的方法阻塞当前线程;

实际还是调用自己重写的tryAcquireShared()方法

三种情况恢复运行态:

(1)其他线程调用countDown()方法,使得计数器为0,返回true

(2)其他线程调用interrupt(),该线程被打断,抛出InterruptedException异常

(3)时间到,返回false

void countDown()

委托Sync调用AQS的releaseShared()方法

实际还是调用自己重写的tryReleaseShared()方法

用for(;;)循环进行CAS操作,更新state值减1;

当state减1等于0后就唤醒被阻塞的线程

long getCount()获取state的值

2. CyclicBarrier

(1)介绍

  • 也称 回环屏障
  • 之前CountDownLatch的计数器是一次性的,用完就结束了,count的值不能重置
  • 而CyclicBarrier每次count=0后,会重置回原来的参数(构造器传入的那个参数)

(2)用法

  • 构造器
    • CyclicBarrier cyc = new CyclicBarrier(n)
    • CyclicBarrier cyc = new CyclicBarrier(n, new Runnable(()->{...}))
  • 其他线程调用:cyc.await();

(3)原理

  • 内部属性 ReentrantLock:独占锁实现
  • 内部属性 parties:记录线程个数,final类型恒定不变
  • 内部属性 count:一开始count=parties,每次线程调用await()方法后count减1,当count=0后唤醒所有线程,然后重置count=parties

(4)Api

int await()

调用该方法的线程会阻塞

三种情况恢复运行态:

(1)有n个线程都调用了await()方法,所有线程都恢复运行

(2)其他线程调用interrupt()方法

(3)与当前屏障点关联的 Generation对象的 broken 标志被设置为 true 时

boolean await(long, TimeUnit)

四种情况恢复运行态:

(1)有n个线程都调用了await()方法,所有线程都恢复运行,这时返回true

(2)超时,返回false

(2)其他线程调用interrupt()方法

(3)与当前屏障点关联的 Generation对象的 broken 标志被设置为 true 时

int dowait(boolean timed, long nanos)

 


3. Semaphore信号量

(1)介绍

  • 也称 信号量
  • 用来限制能同时访问共享资源的线程上限:即共享资源有多个,也允许多个线程访问,但需要限制数量

(2)用法

  • 构造器定义 n 的信号量,可设置公平锁
//构造1:创建最大上限为n的信号量
Semaphore sem = new Semaphore( n )
//构造2:创建最大上限为n的信号量,且为公平
Semaphore sem = new Semaphore( n ,true)
  • 调用 sem.acquire() 加锁, 调用 sem.release() 解锁
    public static void main(String[] args) {
        Semaphore sem = new Semaphore(3);

        for(int i =0; i<10; i++){
            new Thread(()->{
                try{
                    sem.acquire();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try{
                    sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    sem.release();
                }
            }).start();
        }
    }

(3)原理

  • 和ReentrantLock类基本一样,定义了Sync静态内部类(NonfairSync 和 FairSync)
  • 不同的是state为共享模式

 

(4)应用

  • 使用Semaphore限流,让请求线程阻塞,高峰过去后再释放许可。可以用它改进数据库连接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值