Semaphore-学习

88 阅读3分钟

简介

Semaphore用于控制访问资源的数量,可以理解为限流。比如数据库的最大连接数;

Semaphore是共享锁,支持公平锁与非公平锁

内部类Sync继承AQS,实现了获取、释放资源逻辑

基本组成

  • permits

初始化时传入,代表允许访问资源的最大数量,会设置到AQS.state

  • Sync

同步对象,继承AQS,实现共享锁对应的方法:tryAcquireShared、tryReleaseShared

  • FairSync

公平的同步对象,继承Sync

  • NonfairSync

非公平的同步对象,继承Sync

方法

  • acquire
/**
 * Semaphore#acquire
 * 获取资源,失败会阻塞 
 */
public void acquire() throws InterruptedException {
    // 调用AQS模版方法
    sync.acquireSharedInterruptibly(1);
}
​
  • tryAcquireShared
/**
 * FairSync#tryAcquireShared 公平锁获取资源
 */
protected int tryAcquireShared(int acquires) {
    for (;;) {
        // 判断当前线程是否有前节点,true代表有
        if (hasQueuedPredecessors())
            return -1;
        int available = getState();
        // 剩余资源数 = AQS.state - 本次需要获取的资源数
        int remaining = available - acquires;
        // 剩余资源数<0 或 剩余资源数<0时,CAS设置剩余资源数到AQS.state成功,返回剩余资源数
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}
  • nonfairTryAcquireShared
/**
 * Sync#nonfairTryAcquireShared 非公平锁获取资源
 * 与公平锁区别就是没有AQS#hasQueuedPredecessors判断
 */
final int nonfairTryAcquireShared(int acquires) {
    for (;;) {
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}
  • release
public void release() {
    // 调用AQS#releaseShared,模版方法
    sync.releaseShared(1);
}
  • tryReleaseShared
/**
 * Sync#tryReleaseShared
 * 通过自旋+CAS,更新最新的数量 = 当前+释放
 */
protected final boolean tryReleaseShared(int releases) {
    for (;;) {
        // 获取当前AQS.state
        int current = getState();
        // 下一个数量 = 当前数量 + 释放的数量
        int next = current + releases;
        if (next < current) // overflow
            throw new Error("Maximum permit count exceeded");
        // CAS更新AQS.state
        if (compareAndSetState(current, next))
            return true;
    }
}

问题

  1. Hystrix使用到了Semaphore了吗?

Demo

@Slf4j
public class SemaphoreDemo {
​
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(2);
        for (int i = 0; i < 20; i++) {
            new Thread(new Task(semaphore), String.valueOf(i)).start();
        }
    }
​
    static class Task implements Runnable {
​
        private Semaphore semaphore;
​
        public Task(Semaphore semaphore) {
            this.semaphore = semaphore;
        }
​
        @Override
        public void run() {
​
            try {
                log.info("线程: {},准备获取资源", Thread.currentThread().getName());
                semaphore.acquire();
                log.info("线程: {},获取到资源", Thread.currentThread().getName());
                Thread.sleep(5000);
                log.info("线程: {},资源获取完毕,准备释放", Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                semaphore.release();
            }
        }
    }
}
​
// 输出
​
线程: 2,准备获取资源
线程: 1,准备获取资源
线程: 0,准备获取资源
线程: 2,获取到资源
线程: 0,获取到资源
线程: 6,准备获取资源
线程: 5,准备获取资源
线程: 9,准备获取资源
线程: 3,准备获取资源
线程: 8,准备获取资源
线程: 4,准备获取资源
线程: 7,准备获取资源
线程: 0,资源获取完毕,准备释放
线程: 2,资源获取完毕,准备释放
线程: 1,获取到资源
线程: 6,获取到资源
线程: 6,资源获取完毕,准备释放
线程: 1,资源获取完毕,准备释放
线程: 9,获取到资源
线程: 5,获取到资源
线程: 5,资源获取完毕,准备释放
线程: 9,资源获取完毕,准备释放
线程: 3,获取到资源
线程: 8,获取到资源
线程: 8,资源获取完毕,准备释放
线程: 3,资源获取完毕,准备释放
线程: 4,获取到资源
线程: 7,获取到资源
线程: 7,资源获取完毕,准备释放
线程: 4,资源获取完毕,准备释放
​