共享锁,用于类似多个窗口排队买票的场景,可以同时进行多个线程的处理,超出的入队等待

    实现:

    1. /*
    2. 业务场景:
    3. 现在又20个人排队买票,但是窗口只有三个,一个窗口一次只能让一个线程买票
    4. */
    5. Semaphore semaphore = new Semaphore(3);
    6. for (int i = 0; i < 20; i++) {
    7. new Thread(()->{
    8. try {
    9. // 加锁
    10. semaphore.acquire();
    11. System.out.println(Thread.currentThread().getName() + "正在买票");
    12. Thread.sleep(5000L);
    13. System.out.println(Thread.currentThread().getName() + "买票完成");
    14. } catch (InterruptedException e) {
    15. e.printStackTrace();
    16. }finally {
    17. // 释放
    18. semaphore.release();
    19. }
    20. }).start();
    21. }
    22. 输出:
    23. Thread-0正在买票
    24. Thread-1正在买票
    25. Thread-2正在买票
    26. 5S:
    27. Thread-1买票完成
    28. Thread-2买票完成
    29. Thread-3正在买票
    30. Thread-0买票完成
    31. Thread-4正在买票
    32. Thread-5正在买票
    33. ...

    上面例子中给sleep添加一个条件可以看出一个线程释放了立马就会有一个线程就执行

    1. if(!Thread.currentThread().getName().equals("Thread-0")){
    2. Thread.sleep(5000L);
    3. }

    原理
    默认实现的非公平的实现方式
    image.png
    同时也能传入参数自行调整公平与非公平
    image.png
    初始化时会给state赋值,值为传入的参数,设置信号量

    加锁过程:

    1. semaphore.acquire();
    2. public void acquire() throws InterruptedException {
    3. sync.acquireSharedInterruptibly(1);
    4. }
    5. public final void acquireSharedInterruptibly(int arg)
    6. throws InterruptedException {
    7. if (Thread.interrupted())
    8. throw new InterruptedException();
    9. // tryAcquireShared中尝试加锁
    10. if (tryAcquireShared(arg) < 0)
    11. // 没有拿到锁就入队
    12. doAcquireSharedInterruptibly(arg);
    13. }
    14. // 获取资源的逻辑
    15. // acquires == 1
    16. final int nonfairTryAcquireShared(int acquires) {
    17. for (;;) {
    18. // 拿到现在的state
    19. int available = getState();
    20. // 减去传入的间隔
    21. int remaining = available - acquires;
    22. if (remaining < 0 ||
    23. compareAndSetState(available, remaining)) // CAS修改
    24. return remaining;
    25. }
    26. }

    Semaphore在解锁的时候哟传播的特性,当先线程唤醒了修改了state的值去执行,他会判断当下线程的下一个节点的waitState是不是-1(-1代表可以被唤醒),要是等于-1就唤醒下一个线程去尝试竞争资源,不行就休眠