首页 > 代码库 > JUC回顾之-Semaphore底层实现和原理

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);    // 创建10个许可,允许10个并发执行    private static Semaphore s = new Semaphore(5);    public static void main(String[] args) {        for (int i = 0; i < THREAD_COUNT; i++) {            executorService.execute(new Runnable() {                @Override                public void run() {                    try {                        s.acquire();                        System.out.println("线程" + Thread.currentThread().getName() + " 保存数据");                        Thread.sleep(2000);                        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 释放许可

 

 

 

<style></style>

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