JUC-Semaphore使用及原理分析

标签: Semaphore  java信号量

1 Semaphore简介及使用

ps:要想理解Semaphore原理,需要先了解AQS,不了解AQS的可以看先之前的文章->aqs源码解析

Semaphore类是一个计数信号量,必须由获取它的线程释放,底层是基于AQS实现的,通常用于限制可以访问某些资源(物理或逻辑的)线程数目。

举个例子,某个停车场总共有5个停车位,意味着同一时刻最多只能容纳5辆车,先来的5辆车占了车位之后,其他的车只能等待或者离开。下面是代码演示:

public class SemaphoreDemo {

    public static void main(String[] args) {
    	// 初始化 相当于5个车位
        Semaphore semaphore = new Semaphore(5);
        // 10辆车同时抢占车位
        for (int i=0; i<10; i++){
            new Car(i, semaphore).start();
        }
    }

    static class Car extends Thread{

        private Integer num;

        private Semaphore semaphore;

        public Car(Integer num, Semaphore semaphore){
            this.num = num;
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println("第" + num +"辆车 抢占一个车位 ");
                TimeUnit.SECONDS.sleep(1);
                System.out.println("第" + num +"辆车 开走了 ");
                semaphore.release();
            }catch (InterruptedException e){

            }
        }
    }

}

运行结果:
在这里插入图片描述
同一时刻最多容纳5辆车,当车位释放时,才可以有新的车去抢占车位。

2 源码分析

2.1 Semaphore初始化

		Sync(int permits) {
            setState(permits);
        }

当通过构造初始化的时候,调用setState对state变量进行赋值,这个state相当于车位数(限流数)

2.2 acquire方法

	public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
	public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
            // 尝试获取车位
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
    
	final int nonfairTryAcquireShared(int acquires) {
         for (;;) {
         	// 获取限流数
             int available = getState();
             // 可用车位 - 本次获取车位 = 剩余可用车位
             int remaining = available - acquires;
             if (remaining < 0 ||
             	 // cas方式对变量进行修改
                 compareAndSetState(available, remaining))
                 return remaining;
         }
     }

调用acquire方法,默认每次值获取一个共享资源(相当于车位),通过自旋 + cas的方式,修改剩余可用共享资源的个数。

2.3 release方法

	public void release() {
        sync.releaseShared(1);
    }
    
	public final boolean releaseShared(int arg) {
		// 尝试释放资源 
        if (tryReleaseShared(arg)) {
        	// 如果释放成功 则唤醒等待队列中的线程
            doReleaseShared();
            return true;
        }
        return false;
    }
    
	protected final boolean tryReleaseShared(int releases) {
		// 自旋 + cas 的方式修改资源数
        for (;;) {
        	  // 获取当前state
              int current = getState();
              // 基数本次释放后 state的值
              int next = current + releases;
              if (next < current) // overflow
                  throw new Error("Maximum permit count exceeded");
              // 修改
              if (compareAndSetState(current, next))
                  return true;
          }
      }

调用release方法进行释放资源,通过修改state的值进行释放资源,如果释放成功后,唤醒等待队列中的线程去竞争资源。

原文链接:加载失败,请重新获取