Semaphore详解

1、方法

public void acquire()
public void acquireUninterruptibly()
public void release() 
public boolean tryAcquire(int permits)

2、原理
Semaphore原理比较简单,类似ReentrantLock,使用AQS的模板模式来进行实现。

//Semaphore.java
abstract static class Sync extends AbstractQueuedSynchronizer。。。

Sync有两种子类,一个公平,一个非公平。

Semaphore构建通常使用如下构造方法,默认是使用非公平模式。

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

2-1、acquire
还是借助AQS的双向等待队列来实现。

public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
       throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
        //拿state,失败进入核心等待方法
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}

下面来看核心的等待方法。

private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
    //入队列
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        //CAS自旋volatile变量
        for (;;) {
            final Node p = node.predecessor();//前一个节点
            //如果是老二,可以做动作,否则进行park
            if (p == head) {
                //尝试取state,且state-arg。
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            //在ReentratLock研究过这一段,就是遍历队列,然后park
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

2-2、release
release更简单,就是释放state,然后队列下一节点进行unpark即可,由于肯定当前线程执行,都不需要考虑并发。

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

先来看tryReleaseShared,就是state+arg

protected final boolean tryReleaseShared(int releases) {
    for (;;) {
        int current = getState();
        int next = current + releases;
        if (next < current) // overflow
            throw new Error("Maximum permit count exceeded");
        if (compareAndSetState(current, next))
            return true;
    }
}

再来看doReleaseShared,核心就是唤醒下一个节点。

private void doReleaseShared() {
   /*
     * Ensure that a release propagates, even if there are other
     * in-progress acquires/releases.  This proceeds in the usual
     * way of trying to unparkSuccessor of head if it needs
     * signal. But if it does not, status is set to PROPAGATE to
     * ensure that upon release, propagation continues.
     * Additionally, we must loop in case a new node is added
     * while we are doing this. Also, unlike other uses of
     * unparkSuccessor, we need to know if CAS to reset status
     * fails, if so rechecking.
     */
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            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;                // loop on failed CAS
        }
        if (h == head)                   // loop if head changed
            break;
    }
}

通过ReentrantLock看懂了AQS后,深深感觉Semaphore没啥东西。。。

### Python `multiprocessing` 中 `Semaphore` 的使用详解 #### 什么是 `Semaphore` `Semaphore` 是一种同步原语,用于控制对共享资源的并发访问。它可以限制同时进入某个临界区的线程或进程数量。通过初始化时指定的一个计数值,`Semaphore` 可以允许多个进程/线程竞争同一资源[^1]。 当一个进程调用 `acquire()` 方法时,如果当前可用的信号量大于零,则该进程会获得信号量并将其减一;否则,该进程会被阻塞直到有其他进程释放信号量(即调用 `release()` 方法)[^2]。 #### 原理分析 - **初始值设置**:在创建 `Semaphore` 对象时可以通过参数设定其最大允许同时运行的任务数。 - **获取资源 (`acquire`) 和释放资源 (`release`)**: - 调用 `acquire()` 表示请求占用一个单位的资源。 - 如果此时剩余可分配资源不足,则调用方将被挂起等待。 - 当不再需要此资源时应显式地调用 `release()` 来归还所占有的资源单元给系统以便后续任务能够继续执行下去[^3]。 #### 示例代码展示 以下是基于官方文档以及实际应用场景构建的一段演示如何利用 `multiprocessing.Semaphore` 实现多进程之间协调工作的例子: ```python from multiprocessing import Semaphore, Process import time def task(name, sem): """模拟耗时操作""" print(f"{name}: 尝试获取信号量...") # 请求获取信号量 sem.acquire() try: print(f"{name}: 成功获取到信号量.") # 模拟业务逻辑处理过程 time.sleep(2) print(f"{name}: 完成工作.") finally: # 不管发生什么情况都要记得释放信号量 sem.release() if __name__ == "__main__": # 创建一个只允许两个进程同时进入的关键区域 semaphore = Semaphore(2) jobs = [] for i in range(5): job = Process(target=task, args=(f"Worker-{i}", semaphore)) jobs.append(job) job.start() for j in jobs: j.join() ``` 在这个脚本里我们定义了一个名为 `task` 的函数代表每一个独立的工作项,在这个过程中它们都需要先尝试取得由变量 `semaphore` 所表示的那个互斥锁才能正式开始自己的主要活动——这里为了简化描述就让每个工作者睡两秒钟作为他们各自要做的具体事情。注意每次循环都会新启一个新的子进程来负责不同的实例化后的作业对象,并且最后还要确保主线程等到所有的这些分支都结束了才退出整个应用程序。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值