(2.1.27.12)Java并发编程:Lock之Semaphore共享式不可重入锁

版权声明:本文为博主原创文章,转载请注明出处 https://blog.csdn.net/fei20121106/article/details/83268604

Semaphore是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做完自己的申请后归还,超过阈值后,线程申请许可信号将会被阻塞

一、使用示例

	// 创建一个计数阈值为5的信号量对象
		// 只能5个线程同时访问
		Semaphore semp = new Semaphore(5);
 
		try {
			// 申请许可
			semp.acquire();
			try {
				// 业务逻辑
			} catch (Exception e) {
 
			} finally {
				// 释放许可
				semp.release();
			}
		} catch (InterruptedException e) {
 

二、总体结构

public class Semaphore implements java.io.Serializable {

    private final Sync sync;

    public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }
    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }



    abstract static class Sync extends AbstractQueuedSynchronizer {}
	
    static final class NonfairSync extends Sync {}
	
    static final class FairSync extends Sync {}
	
	
	//[Lock接口]获取锁. 成功则向下运行,失败则阻塞
    public void acquire(int permits) throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
    }

	//[Lock接口]可中断地获取锁,在当前线程获取锁的过程中可以响应中断信号
    public void acquireUninterruptibly(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireShared(permits);
    }

	//[Lock接口]尝试非阻塞获取锁,调用方法后立即返回,成功返回true,失败返回false
    public boolean tryAcquire(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        return sync.nonfairTryAcquireShared(permits) >= 0;
    }

	//[Lock接口]在超时时间内获取锁,到达超时时间将返回false,也可以响应中断
    public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
        throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
    }

	//[Lock接口]释放锁
    public void release(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
    }

	//[Lock接口]获取等待通知组件实现信号控制,等待通知组件实现类似于Object.wait()方法的功能
    public Condition newCondition() {
        return this.sync.newCondition();
    }
	
}

通过代码我们可以看到,基本全部的业务都是交给 Sync去实现的

  • Sync类是AQS的子类,而NonfairSync和FairSync是Sync的子类。
  • 公平锁和非公平锁的区别就在于获取锁时候的逻辑略有不同,其他操作都是一样的,因此公用的操作都放在Sync类里,NonfairSync和FairSync里只是实现自己的tryAcquireShared(int acquires)方法。

值得注意的是:

通常而言”AQS里的state在重入锁里代表线程重入的次数,state=1代表重入锁当前已被某个线程独占,这个线程每重入一次,state++“,但是在Semaphore中:

  1. state在AQS被实例化时构建为 允许共享的次数
  2. state>0 表示 还可以被共享占有

三、acquire()和release()大致流程

acquire()获取锁的主要流程如下:

  1. 首先,Semaphore 的 acquire() 方法会调用其内部成员变量sync的 acquireShared() 方法;
  2. 其次,sync的非公平锁NonfairSync或公平锁FairSync实现了父类AbstractQueuedSynchronizer的相关钩子方法

3.1 AQS的实现

我们回忆一下AQS需要重写的钩子方法:

方法名称 描述
boolean tryAcquire(int arg) 独占式尝试获取同步状态(通过CAS操作设置同步状态),如果成功返回true,反之返回false
boolean tryRelease(int arg) 独占式释放同步状态,成功返回true,失败返回false。
int tryAcquireShared(int arg) 共享式的获取同步状态,返回大于等于0的值,表示获取成功,反之失败。
boolean tryReleaseShared(int arg) 共享式释放同步状态,成功返回true,失败返回false。
boolean isHeldExclusively() 判断同步器是否在独占模式下被占用,一般用来表示同步器是否被当前线程占用

我们来看下Sync对其的实现,可以看出Sync中:

  1. 实例化时,重置 state的值
  2. 重写了tryReleaseShared共享式释放同步状态
  3. 并没有重写tryAcquireShared(int arg)共享式尝试获取同步状态方法, 而是交给子类去实现
  4. 新建了nonfairTryAcquireShared(),这其实是个同步状态获取方法,在”非公平模式“的子类中被调用
  5. 一系列的Utils方法
abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 1192457210091910933L;

		/**************1. 实例化时,重置 state的值*********/
        Sync(int permits) {
            setState(permits);
        }
		
		/**************2. 重写了`tryRelease`独占式释放同步状态*********/
		protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                if (next < current) // 释放超支
                    throw new Error("Maximum permit count exceeded");
					
				//共享式锁的【更新锁状态】不能通过setState,而是通过CAS操作。  
				//这是由于独享式锁释放时肯定是单线程模型的,而共享式的则可能是多线程模型
                if (compareAndSetState(current, next))
                    return true;
            }
        }
		
		
		/******3. 并没有重写`tryAcquire(int arg)`独占式尝试获取同步状态方法, 而是交给子类去实现***********/

		/******4. 新建了`nonfairTryAcquire()`,这其实是个同步状态获取方法,在”非公平模式“的子类中被调用*****/
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

       /**************5. 一系列的Utils方法********************/
		final int getPermits() {
            return getState();
        }
		
		//自旋式减少 共享信号
		//Semaphore还可以减小许可数量的方法,该方法可以用于用于当资源用完不能再用时,这时就可以减小许可证
        final void reducePermits(int reductions) {
            for (;;) {
                int current = getState();
                int next = current - reductions;
                if (next > current) // underflow
                    throw new Error("Permit count underflow");
                if (compareAndSetState(current, next))
                    return;
            }
        }

		//自旋式重置 共享信号为0
		//Semaphore还可以一次将剩余的许可数量全部取走,返回剩余数量
        final int drainPermits() {
            for (;;) {
                int current = getState();
                if (current == 0 || compareAndSetState(current, 0))
                    return current;
            }
        }
    }

3.1.1 非公平锁

  1. 通过基于CAS操作的compareAndSetState(available, remaining))方法,试图修改当前锁的状态
    • 这个上来就CAS的操作也是非公共锁的一种体现,CAS操作成功的话,则将当前线程设置为该锁的唯一拥有者。
    static final class NonfairSync extends Sync {

        NonfairSync(int permits) {
            super(permits);
        }

        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
		
	    final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 || // >0时表示有可用资源,才进行CAS竞争锁
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
    }

3.1.2 公平锁

我们可以发现,这段代码与上面的 nonfairTryAcquire方法就只多了一句代码,而就是这一句代码就实现了公平锁。

hasQueuedPredecessors()方法判断同步队列中是否有更早开始等待锁的线程。如果有,则直接返回。

   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;
            }
        }
    }

猜你喜欢

转载自blog.csdn.net/fei20121106/article/details/83268604