基本介绍

多线程中上锁的目的一般是一种对受限资源的保护,例如:保证同一时刻只有一个线程能访问的ReentrantLock或者保证只有一个线程可以写入的ReadWriteLock

还有一种受限资源,它需要保证同一时刻最多有N个线程能访问,比如同一时刻最多创建100个数据库连接,最多允许10个用户下载等

这种限制数量的锁,如果用Lock数组来实现,就太麻烦了。

这种情况就可以使用Semaphore,例如,最多允许2个线程同时访问:

代码测试

public class TestSemaphore {
    public static void main(String[] args) throws InterruptedException {
        AccessLimitControl control = new AccessLimitControl();
        List<Thread> tList = new ArrayList<>();
        for (int i = 0; i< 5; i++) {
            Thread t = new Thread(() -> {
                try {
                    String access = control.access();
                    System.out.println("线程:" + Thread.currentThread().getName() + " 的返回值:" + access);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            tList.add(t);
        }
        for (Thread thread : tList) {
            thread.start();
        }
        for (Thread thread : tList) {
            thread.join();
        }
    }

    static class AccessLimitControl {
        // 任意时刻仅允许最多3个线程获取许可:
        final Semaphore semaphore = new Semaphore(1);
        public String access() throws Exception {
            // 如果超过了许可数量,其他线程将在此等待:
            System.out.println("进入队列的线程:" + Thread.currentThread().getName());
            semaphore.acquire();
            System.out.println("当前线程名称:" + Thread.currentThread().getName());
            try {
                Thread.sleep(2000);
                return UUID.randomUUID().toString();
            } finally {
                semaphore.release();
            }
        }
    }
}

代码运行,以下看似 Thread-3 和 Thread-1 同时进入,其实是 Thread-3 return 后打印结果时,Thread-1 立刻进入了

进入队列的线程:Thread-3
进入队列的线程:Thread-4
进入队列的线程:Thread-1
进入队列的线程:Thread-2
进入队列的线程:Thread-0
当前线程名称:Thread-3
当前线程名称:Thread-1
线程:Thread-3 的返回值:997edc5d-7ec4-4160-a3b7-0d4a76fbb2ff
线程:Thread-1 的返回值:ba87b784-66d0-4073-9967-ccf44df4214a
当前线程名称:Thread-2
线程:Thread-2 的返回值:5c784f8c-fef9-4fa8-83cc-98ee2e7e6238
当前线程名称:Thread-4
线程:Thread-4 的返回值:376938f5-98ab-49da-9402-566523f31ed0
当前线程名称:Thread-0
线程:Thread-0 的返回值:f51391af-649a-404a-a2d7-68b073d44ec0

小结

Semaphore本质上就是一个信号计数器,用于限制同一时间的最大访问数量,如果要对某一受限资源进行限流访问,可以使用Semaphore,保证同一时间最多N个线程访问受限资源。