Semaphore原理、源码分析

Semaphore 类比为停车场,其 permits 代表停车位。线程获取 permits 即获得车位,导致可用车位减少。文章探讨了 Semaphore 的不公平实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Semaphore 有点像一个停车场,permits 就好像停车位数量,当线程获得了 permits 就像是获得了停车位,然后停车场显示空余车位减一

此处是不公平的实现

	static final class NonfairSync extends Sync {
	
	private static final long serialVersionUID = -2694183684443567898L;
	
	//构造方法,permits等于资源数
	NonfairSync(int permits) {
		super(permits);
	}
	
	// Semaphore 方法, 方便阅读, 放在此处
	public void acquire() throws InterruptedException {
		sync.acquireSharedInterruptibly(1);
	}
	
	// AQS 继承过来的方法, 方便阅读, 放在此处
	// 获得可打断的共享锁
	public final void acquireSharedInterruptibly(int arg)
			throws InterruptedException {
		//如果发生中断则抛出异常
		if (Thread.interrupted())
			throw new InterruptedException();
		if (tryAcquireShared(arg) < 0)
			//如果资源不够,进入此方法
			doAcquireSharedInterruptibly(arg);
	}
 
	// 尝试获得共享锁
	protected int tryAcquireShared(int acquires) {
		return nonfairTryAcquireShared(acquires);
	}
	
	// Sync 继承过来的方法, 方便阅读, 放在此处
	// 以不公平的方式尝试获取共享锁
	final int nonfairTryAcquireShared(int acquires) {
		for (;;) {
			int available = getState();//剩下的资源数
			int remaining = available - acquires; //分配完还剩多少资源
			if (
				// 如果资源已经用完, 返回负数, 表示获取失败, 进入 doAcquireSharedInterruptibly
				remaining < 0 ||
				// 如果资源足够,cas重试成功, 返回正数, 表示获取成功
				compareAndSetState(available, remaining)
			 ) {
				return remaining;
			}
		}
	}
 
	// AQS 继承过来的方法, 方便阅读, 放在此处
	// 资源不够,进入AQS队列
	private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {
		final Node node = addWaiter(Node.SHARED);
		boolean failed = true;
		try {
			for (;;) {
				final Node p = node.predecessor();
				if (p == head) {
					// 再次尝试获取许可
					int r = tryAcquireShared(arg);
					if (r >= 0) {
						// 成功后本线程出队(AQS), 所在 Node设置为 head
						// 如果 head.waitStatus == Node.SIGNAL ==> 0 成功, 下一个节点 unpark
						// 如果 head.waitStatus == 0 ==> Node.PROPAGATE 
						// r 表示可用资源数, 为 0 则不会继续传播
						setHeadAndPropagate(node, r);
						p.next = null; // help GC
						failed = false;
						return;
					}
				}
				// 不成功, 设置上一个节点 waitStatus = Node.SIGNAL, 下轮进入 park 阻塞
				if (shouldParkAfterFailedAcquire(p, node) &&
					parkAndCheckInterrupt())
						throw new InterruptedException();
				}
			}
		} finally {
			if (failed)
			cancelAcquire(node);
		}
	}
	
	// Semaphore 方法, 方便阅读, 放在此处
	// 默认一次释放一个资源
	public void release() {
		sync.releaseShared(1);
	}
 
	// AQS 继承过来的方法, 方便阅读, 放在此处
	public final boolean releaseShared(int arg) {
		if (tryReleaseShared(arg)) {
			//如果释放资源成功,则让AQS中的node获取资源
			doReleaseShared();
			return true;
		}
		return false;
	}
	
	// Sync 继承过来的方法, 方便阅读, 放在此处
	protected final boolean tryReleaseShared(int releases) {
		for (;;) {
			int current = getState();//现在有多少资源
			int next = current + releases;//释放之后有多少资源
			if (next < current) // releases < 0
				throw new Error("Maximum permit count exceeded");
			//以cas的方式把state与current比较后设置为next,即释放后的资源
			if (compareAndSetState(current, next))
				return true;
		}
	}

	//在tryReleaseShared成功后,让AQS中的node获取资源
    private void doReleaseShared() {
    	//死循环,只有发现h没有变化时才会退出
    	//h一定会变化是因为获取了资源
        for (;;) {
            Node h = head;
            //判断是否至少有两个node,才能至少有一个可以获取资源的node
            //0个说明head=tail=null,还没初始化
            //1个说明已经初始化了dummy
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                //如果状态为signal,说明h的后继是需要通知的
                //只要h成功的从signal修改为0,那么后续节点也会被唤醒
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;              
            }
            //如果状态为PROPAGATE,直接判断head是否变化
            if (h == head)
                break;
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值