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. 面对过去无怨无悔,把握现在充满信心,备战未来无所畏惧。保持信念,克服恐惧!一点一滴的积累,一点一滴的沉淀,学技术需要不断的积淀!
posted @ 2021-06-17 23:03  hanease  阅读(98)  评论(0编辑  收藏  举报