Semaphore

简称信号量,处理基于空闲信号的同步情况,经常应用在限流的场景中;
定义了资源总量 state=permits , 当 state>O 时就能获得锁 , 并将 state 减 l ,
当 state=O 时只能等待其他线程释放锁 , 当释放锁时 state 加 l , 其他等待线程又能获得 这个锁。
当 Semphore 的 permits 定义为 l 时,就是互斥锁,当 permits> I 就是共享锁。

生活中例子:火车站限流进站
假设某火车站的进站通道共有 3 个窗口,一批需要进站的人排成长队,每个 人相当于一个线程。
当 3 个窗口中的任意一个出现空闲时,工作人员指示队列中第一个人出队到该空闲窗口接受查验。

api用法:
调用 Semaphore 对象的 acquire()成功后,才可以往下执行,完成后执 行 release()释放持有的信号量,下一个线程就可以马上获取这个空闲信号量进入执行。

acquire()源码分析:
结合ReentrantLock,加锁解锁的逻辑是由具体的锁实现,入队堵塞的逻辑是共用的,由AQS提供
加锁:
image.png
解锁:和独占锁不一样的实现,如果是共享模型,一个线程被唤醒并获取锁成功后继续唤醒后续线程去争夺资源,因为Semaphore可以一次释放多个资源
image.png

CountDownLatch

CountDownLatch 是基于执行时间的同步类,协调线程执行的先后时序
场景:1)让多个线程等待,模型并发,让多个线程一起执行
2)让单个线程等待,多个线程执行完成后,再让那个等待线程执行,可以用于汇总合并。 这种在工作中经常用到,比如批量任务扔到线程池中去执行,最后再将任务的结果归并,可以用于减少接口响应rt。
CountDownLatch与Thread.join的区别
a线程执行的代码中有逻辑是b线程调用join 方法,a线程需要等待线程b 执行完毕才能继续执行,
而CountDownLatch只需要检查计数器的值为零就可以继续向下执行,相比之下,CountDownLatch更加灵活

CountDownLatch是通过AQS的“共享锁”实现,共享锁没有重入
加锁关键代码:
只要state不为0 就入队等待

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

解锁关键代码:
只有state减少到0时才会释放锁

        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }

死锁:
解决死锁问题:打破循环等待,比如超时释放锁