Java并发编程之Semaphore源码分析

Semaphore介绍

Semaphore是JDK1.5提供容许一组拿到许可证的线程访问共享资源,并禁止其余拿不到许可证的线程访问共享资源工具。Semaphore通常用来作系统的限流。java

特色

Semaphore和ReentrantLock功能很相似都是限制线程访问共享资源并且都有公平锁和非公平锁模式。不一样点以下表格:工具

Semaphore ReentrantLock
容许线程访问共享资源个数 可配多个 1个
可重入

Semaphore原理分析

Semaphore的实现是ReentrantLock和CyclicBarrier的结合体,不少源码跟ReentrantLock和CyclicBarrier同样。源码分析

acquire()源码分析

acquire()源码以下ui

public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

acquire()是直接调用了AQS里的acquireSharedInterruptibly(1)方法咱们来看下acquireSharedInterruptibly(1)方法.net

public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
	//判断当前线程是否被中断,中断的话抛出异常
        if (Thread.interrupted())
            throw new InterruptedException();
	//判断当前计数器是否为0是的话返回1不然返回-1
        if (tryAcquireShared(arg) < 0)
	//加入到同步阻塞队列等待被唤醒
            doAcquireSharedInterruptibly(arg);
    }

先判断当前线程是否被中断,中断的话抛出异常。而后调用tryAcquireShared(arg)减许可证并返回剩余许可证,这里若是许可证为0了就表示许可证已经用完须要进行阻塞等待,不然获取到许可证,则调用 doAcquireSharedInterruptibly(arg)加入到同步阻塞队列等待被唤醒。线程

tryAcquireShared(arg)的实现分公平模式和非公平模式,先看下默认非公平模式的实现:code

protected int tryAcquireShared(int acquires) {
     return nonfairTryAcquireShared(acquires);
}
final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
			//获取当前许可证
                int available = getState();
			//减许可证
                int remaining = available - acquires;
			//若是小于0直接返回不然CAS替换剩余值
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

实现很简单就是减掉一个许可证,并返回剩余许可证。blog

再看下公平模式的实现:队列

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

该方法跟非公平锁基本都同样,只是在获取锁的时候加了hasQueuedPredecessors()判断,这个方法主要判断了当前线程是否在头节点的下个节点,这样保证了获取锁的顺序性。资源

doAcquireSharedInterruptibly(arg)方法在以前文章已经讲过,这里再也不累述连接

release()源码分析

public void release() {
   sync.releaseShared(1);
}

release()是直接调用了AQS里的releaseShared(1)方法咱们来看下releaseShared(1)方法

releaseShared(1)源码以下

public final boolean releaseShared(int arg) {
 		//许可证加1并返回是否成功
        if (tryReleaseShared(arg)) {
		//唤醒同步阻塞队列中的头节点的下个节点线程
            doReleaseShared();
            return true;
        }
        return false;
    }

先调用tryReleaseShared(arg)将许可证加1并返回是否成功,若是是调用doReleaseShared()唤醒同步阻塞队列中的头节点的下个节点线程。

下面来看下tryReleaseShared(arg)方法

protected final boolean tryReleaseShared(int releases) {
            for (;;) {
		//获取当前许可证
                int current = getState();
		//获取许可证加releases
                int next = current + releases;
		//加了之后的值比原来的值小,说明releases传的是负数,直接抛出异常
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
		//CAS替换原来的值
                if (compareAndSetState(current, next))
                    return true;
            }
        }

实现很简单就是增长一个许可证,并用CAS替换掉原来的值,若是失败自旋直至成功。

doReleaseShared()方法在以前文章已经讲过,这里再也不累述连接

相关文章
相关标签/搜索