zoukankan      html  css  js  c++  java
  • 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 释放许可
  • 相关阅读:
    【搜索引擎】Solr最新安装以及通过关系型数据库(MySQL,Oracle,PostgreSQL)导入数据
    【搜索引擎】SOLR VS Elasticsearch(2019技术选型参考)
    【Java源码】集合类-优先队列PriorityQueue
    【Java源码】树-概述
    Go语言调度器之创建main goroutine(13)
    Go语言goroutine调度器初始化(12)
    Go语言goroutine调度器概述(11)
    线程本地存储及实现原理
    操作系统线程及线程调度
    系统调用
  • 原文地址:https://www.cnblogs.com/200911/p/6060359.html
Copyright © 2011-2022 走看看