• JUC回顾之-Semaphore底层实现和原理


    1.控制并发线程数的Semaphore

       Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,保证合理的使用公共资源。

       线程可以通过acquire()方法来获取信号量的许可,当信号量中没有可用的许可的时候,线程阻塞,直到有可用的许可为止。线程可以通过release()方法释放它持有

       的信号量的许可。

    2.Semaphore的方法列表:

    复制代码
    // 创建具有给定的许可数和非公平的公平设置的 Semaphore。
    Semaphore(int permits)
    // 创建具有给定的许可数和给定的公平设置的 Semaphore。
    Semaphore(int permits, boolean fair)
    
    // 从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。
    void acquire()
    // 从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,或者线程已被中断。
    void acquire(int permits)
    // 从此信号量中获取许可,在有可用的许可前将其阻塞。
    void acquireUninterruptibly()
    // 从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞。
    void acquireUninterruptibly(int permits)
    // 返回此信号量中当前可用的许可数。
    int availablePermits()
    // 获取并返回立即可用的所有许可。
    int drainPermits()
    // 返回一个 collection,包含可能等待获取的线程。
    protected Collection<Thread> getQueuedThreads()
    // 返回正在等待获取的线程的估计数目。
    int getQueueLength()
    // 查询是否有线程正在等待获取。
    boolean hasQueuedThreads()
    // 如果此信号量的公平设置为 true,则返回 true。
    boolean isFair()
    // 根据指定的缩减量减小可用许可的数目。
    protected void reducePermits(int reduction)
    // 释放一个许可,将其返回给信号量。
    void release()
    // 释放给定数目的许可,将其返回到信号量。
    void release(int permits)
    // 返回标识此信号量的字符串,以及信号量的状态。
    String toString()
    // 仅在调用时此信号量存在一个可用许可,才从信号量获取许可。
    boolean tryAcquire()
    // 仅在调用时此信号量中有给定数目的许可时,才从此信号量中获取这些许可。
    boolean tryAcquire(int permits)
    // 如果在给定的等待时间内此信号量有可用的所有许可,并且当前线程未被中断,则从此信号量获取给定数目的许可。
    boolean tryAcquire(int permits, long timeout, TimeUnit unit)
    // 如果在给定的等待时间内,此信号量有可用的许可并且当前线程未被中断,则从此信号量获取一个许可。
    boolean tryAcquire(long timeout, TimeUnit unit)
    复制代码

    3.Semaphore的内部结构

    4.Semaphore的源码:

    "公平信号量"和"非公平信号量"的区别

    "公平信号量"和"非公平信号量"的释放信号量的机制是一样的!不同的是它们获取信号量的机制:线程在尝试获取信号量许可时,对于公平信号量而言,如果当前线程不在CLH队列的头部,则排队等候;而对于非公平信号量而言,无论当前线程是不是在CLH队列的头部,它都会直接获取信号量。该差异具体的体现在,它们的tryAcquireShared()函数的实现不同。

    公平信号量tryAcquireShared源码如下:

    复制代码
    /**
         * Fair version
         */
        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;
                }
            }
        }
    复制代码

    非公平信号量tryAcquireShared源码如下:

    复制代码
    /**
         * NonFair version
         */
        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);
            }
        }
    复制代码

     实例:

    复制代码
    public class SemaphoreTest {
        private static final int THREAD_COUNT = 10;
    private static ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT); // 创建5个许可,允许5个并发执行 private static Semaphore s = new Semaphore(5); public static void main(String[] args) {
    //创建10个线程执行任务 for (int i = 0; i < THREAD_COUNT; i++) { executorService.execute(new Runnable() { @Override public void run() { try {
    //同时只能有5个线程并发执行保存数据的任务 s.acquire(); System.out.println("线程" + Thread.currentThread().getName() + " 保存数据"); Thread.sleep(2000);
    //5个线程保存完数据,释放1个许可,其他的线程才能获取许可,继续执行保存数据的任务 s.release(); System.out.println("线程" + Thread.currentThread().getName() + " 释放许可"); } catch (InterruptedException e) { e.printStackTrace(); } } }); } executorService.shutdown(); } }
    复制代码

    结果:10个线程保存数据,但是只允许5个线程并发的执行,当5个线程都保存完数据以后,释放许可,其他线程才能拿到许可继续保存数据,直到10个线程都保存完数据释放许可为止。

    复制代码
    线程pool-1-thread-2 保存数据
    线程pool-1-thread-3 保存数据
    线程pool-1-thread-1 保存数据
    线程pool-1-thread-4 保存数据
    线程pool-1-thread-5 保存数据
    线程pool-1-thread-2 释放许可
    线程pool-1-thread-6 保存数据
    线程pool-1-thread-1 释放许可
    线程pool-1-thread-3 释放许可
    线程pool-1-thread-4 释放许可
    线程pool-1-thread-9 保存数据
    线程pool-1-thread-10 保存数据
    线程pool-1-thread-5 释放许可
    线程pool-1-thread-8 保存数据
    线程pool-1-thread-7 保存数据
    线程pool-1-thread-10 释放许可
    线程pool-1-thread-9 释放许可
    线程pool-1-thread-6 释放许可
    线程pool-1-thread-8 释放许可
    线程pool-1-thread-7 释放许可
    复制代码
    Face your past without regret. Handle your present with confidence.Prepare for future without fear. keep the faith and drop the fear. 面对过去无怨无悔,把握现在充满信心,备战未来无所畏惧。保持信念,克服恐惧!一点一滴的积累,一点一滴的沉淀,学技术需要不断的积淀!
  • 相关阅读:
    redis订阅发布功能
    redis基础知识
    Redis安装启动(linux系统)
    Mysql之Linux中mariadb主从复制
    nginx+vue+uwsgi+django的前后端分离项目部署
    supervisor进程管理工具
    nginx+uwsgi+django
    uwsgi模块以参数形式运行项目
    nginx之启停操作及配置文件语法检测
    nginx配置之负载均衡
  • 原文地址:https://www.cnblogs.com/hanease/p/14897687.html
Copyright © 2020-2023  润新知