Semaphore信号量限制访问

什么是Semaphore

在这里插入图片描述

Semaphore是一个计数信号量,用于控制同时访问特定资源的线程数量,以维护资源的访问控制和确保系统的线程安全。Semaphore可以被视为一个包含若干许可(permit)的集合,线程需要先获取许可才能执行受控操作,执行完毕后归还许可,从而允许其他等待的线程继续执行。

public class Semaphore implements java.io.Serializable {}

主要方法包括:

acquire():尝试获取一个许可,如果没有可用许可,则阻塞直到有其他线程释放许可。
release():释放一个许可,增加信号量的计数,如果其他线程正在等待许可,则唤醒其中一个线程。
tryAcquire():尝试获取一个许可,如果无法立即获取则返回false,不会阻塞。
availablePermits():返回当前可用的许可数。

使用Semaphore

Semaphore 类中的构造函数根据传入的参数初始化 Semaphore 实例。这个构造函数接受两个参数:

  1. permits:表示信号量初始时允许同时访问共享资源的许可证数量。换句话说,就是并发访问的上限。
  2. fair:这是一个布尔值,用来决定信号量是否采用公平策略。
public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

代码示例:

public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(2, false);
        for (int i = 0; i < 8; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "排队");
                    Thread.sleep(1000L);
                } catch (InterruptedException ignore) {
                } finally {
                    semaphore.release();
                }
            }, "排队编号:" + i).start();
        }
    }
排队编号:1排队
排队编号:0排队
排队编号:2排队
排队编号:3排队
排队编号:4排队
排队编号:5排队
排队编号:7排队
排队编号:6排队

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

tryAcquireShared尝试在共享模式下获取锁,doAcquireSharedInterruptibly在共享模式下获取锁。

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) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

该方法首先将节点添加到等待队列中,然后尝试获取共享锁。如果成功获取到共享锁,就将节点设置为头节点并传播,然后将前驱节点的下一个节点设置为null以帮助垃圾回收。如果在尝试获取共享锁时失败,就检查是否需要在失败后挂起线程,并在挂起后检查线程是否被中断。如果线程被中断,就抛出InterruptedException异常。最后,如果获取共享锁失败,就取消获取操作。

release函数

此方法释放一个(多个)许可,将其返回给信号量,源码如下:

public void release() {
    sync.releaseShared(1);
}
 public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

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

doReleaseShared()方法,用于在共享模式下释放锁。它确保即使有其他正在进行的获取/释放操作,释放操作也能传播。通常的做法是尝试唤醒头节点的后继节点(如果需要信号)。但是,如果无法唤醒后继节点,状态将被设置为PROPAGATE,以确保在释放时继续传播。此外,我们必须循环,以防在我们执行此操作时添加新节点。与其他使用unparkSuccessor的情况不同,我们需要知道CAS重置状态是否失败,如果是,则重新检查。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jpq+

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值