• Semaphore 的使用与源码解析


    使用

    信号量(Semaphore)允许多个线程同时访问临界区资源,而 ReentrantLock 这类锁同时只允许一个线程访问临界区资源。

    Semaphore 就是共享锁,它的构造方法可以传一个许可数 permits,表示临界区资源数量,多少个资源同时也最多只能由多少个线程来访问。当 permits 等于 1 时,Semaphore 就等价于 ReentrantLock 这类互斥锁。

    代码示例:

    @Test
    public void test() throws InterruptedException {
        // 定义一个线程池
        ExecutorService executor = Executors.newFixedThreadPool(3);
        int userNuber = 10;
        CountDownLatch countDownLatch = new CountDownLatch(userNuber);
        // 三个许可,表示资源总数,这里表示只有三本书籍,所以最多只能由三个人借阅,即最多只能有三个线程获取到锁
        int permits = 3;
        Semaphore semaphore = new Semaphore(permits);
    
        IntStream.range(0, userNuber).forEach(i -> {
            executor.submit(() -> {
                try {
                    System.out.println("学生" + i + "等待借阅书籍......");
    
                    // 获取一个许可(加锁)
                    semaphore.acquire(1);
    
                    System.out.println("学生" + i + "借阅到了一个本书籍,当前还剩余" + semaphore.availablePermits() + "本书籍");
                    Thread.sleep(i * 500);
    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 释放一个许可(解锁)
                    semaphore.release(1);
                    System.out.println("学生" + i + "归还了一个本书籍,当前还剩余" + semaphore.availablePermits() + "本书籍");
                    countDownLatch.countDown();
                }
            });
        });
    
        countDownLatch.await();
        executor.shutdown();
    
        /**
             * 执行结果:
             *
             * 学生0等待借阅书籍......
             * 学生0借阅到了一个本书籍,当前还剩余2本书籍
             * 学生1等待借阅书籍......
             * 学生1借阅到了一个本书籍,当前还剩余1本书籍
             * 学生2等待借阅书籍......
             * 学生2借阅到了一个本书籍,当前还剩余0本书籍
             * 学生0归还了一个本书籍,当前还剩余1本书籍
             * 学生3等待借阅书籍......
             * 学生3借阅到了一个本书籍,当前还剩余0本书籍
             * 学生1归还了一个本书籍,当前还剩余1本书籍
             * 学生4等待借阅书籍......
             * 学生4借阅到了一个本书籍,当前还剩余0本书籍
             * 学生2归还了一个本书籍,当前还剩余1本书籍
             * 学生5等待借阅书籍......
             * 学生5借阅到了一个本书籍,当前还剩余0本书籍
             * 学生3归还了一个本书籍,当前还剩余1本书籍
             * 学生6等待借阅书籍......
             * 学生6借阅到了一个本书籍,当前还剩余0本书籍
             * 学生4归还了一个本书籍,当前还剩余1本书籍
             * 学生7等待借阅书籍......
             * 学生7借阅到了一个本书籍,当前还剩余0本书籍
             * 学生5归还了一个本书籍,当前还剩余1本书籍
             * 学生8等待借阅书籍......
             * 学生8借阅到了一个本书籍,当前还剩余0本书籍
             * 学生6归还了一个本书籍,当前还剩余1本书籍
             * 学生9等待借阅书籍......
             * 学生9借阅到了一个本书籍,当前还剩余0本书籍
             * 学生7归还了一个本书籍,当前还剩余1本书籍
             * 学生8归还了一个本书籍,当前还剩余2本书籍
             * 学生9归还了一个本书籍,当前还剩余3本书籍
             */
    }
    

    源码解析

    Sync 内部类

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 1192457210091910933L;
    
        // Semaphore 构造函数的传参 permits 赋值给 state
        Sync(int permits) {
            setState(permits);
        }
    
        // 获取许可数(即 state 的值)
        final int getPermits() {
            return getState();
        }
    
        // 非公平锁方式获取共享锁
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                // 当前可用的许可数
                int available = getState();
                // 当前申请锁后剩余的许可数
                int remaining = available - acquires;
                // 如果剩余许可数小于 0 直接返回剩余许可数
                // 如果剩余许可数大于 0,将其设置为 state,如果成功,则返回剩余许可数
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
    
        // 释放锁
        protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                // 当前可用的许可数
                int current = getState();
                // (释放锁后)增加许可数
                int next = current + releases;
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                // 将增加后的许可数赋值给 state,成功则返回,否则自旋重试
                if (compareAndSetState(current, next))
                    return true;
            }
        }
    
        // 减少许可数
        final void reducePermits(int reductions) {
            for (;;) {
                int current = getState();
                int next = current - reductions;
                if (next > current) // underflow
                    throw new Error("Permit count underflow");
                if (compareAndSetState(current, next))
                    return;
            }
        }
    
        // 清空许可数
        final int drainPermits() {
            for (;;) {
                // 当前许可数
                int current = getState();
                // 将 state 设置为 0
                if (current == 0 || compareAndSetState(current, 0))
                    // 返回当前许可数
                    return current;
            }
        }
    }
    

    NonfairSync 内部类

    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -2694183684443567898L;
    
        NonfairSync(int permits) {
            super(permits);
        }
    
        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
    }
    

    FairSync 内部类

    static final class FairSync extends Sync {
        private static final long serialVersionUID = 2014338818796000944L;
    
        FairSync(int permits) {
            super(permits);
        }
    
        protected int tryAcquireShared(int acquires) {
            for (;;) {
                // 如果当前线程节点还有前序节点,则获取锁失败
                if (hasQueuedPredecessors())
                    return -1;
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
    }
    

    构造函数

    // 默认使用非公平锁
    public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }
    
    // permits 表示同时允许访问临界区资源的线程数
    // fair 表示使用公平锁实现还是非公平锁实现
    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }
    

    acquire

    // 可中断地阻塞获取锁
    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    

    acquireUninterruptibly

    // 不可中断地阻塞获取锁
    public void acquireUninterruptibly() {
        sync.acquireShared(1);
    }
    

    tryAcquire

    public boolean tryAcquire() {
        // nonfairTryAcquireShared 返回剩余可用许可数
        // 剩余可用许可数大于等于 0,表示可以获取锁
        return sync.nonfairTryAcquireShared(1) >= 0;
    }
    
    public boolean tryAcquire(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }
    

    release

    // 释放锁(一个许可)
    public void release() {
        sync.releaseShared(1);
    }
    

    acquire

    // 一次获取 permits 个许可(可中断,阻塞获取)
    public void acquire(int permits) throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
    }
    

    acquireUninterruptibly

    // 一次获取 permits 个许可(不可中断、阻塞获取)
    public void acquireUninterruptibly(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireShared(permits);
    }
    

    tryAcquire

    // 一次获取 permits 个许可(可中断,非阻塞获取)
    public boolean tryAcquire(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        return sync.nonfairTryAcquireShared(permits) >= 0;
    }
    
    // 一次获取 permits 个许可(可中断,非阻塞获取、超时可退出)
    public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
        throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
    }
    

    release

    // 释放多个许可
    public void release(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
    }
    
  • 相关阅读:
    前端插件之Datatables使用--上篇
    Django websocket之web端实时查看日志实践案例
    前端插件之Select2使用
    前端插件之Bootstrap Dual Listbox使用
    简单易落地且对运维友好的配置中心方案
    Devops-运维效率之数据迁移自动化
    中小团队基于Docker的Devops实践
    nginx第七篇:Nginx集群(负载均衡)、集群介绍、构建集群、nginx默认分发算法、nginx基于请求头的分发算法
    nginx之旅(第六篇):nginx优化--nginx优化目的、工作进程优化、长连接设置、数据压缩、客户端缓存
    nginx之旅(第五篇):URL重写介绍、URL重写场景、URL重写语法
  • 原文地址:https://www.cnblogs.com/wu726/p/15646785.html
Copyright © 2020-2023  润新知