Semaphore

  • Semaphore

Semaphore也就是我们常说的信号灯,semaphore可以控制同时访问的线程个数,通过acquire获取一个许可,如果没有就等待,通过release释放一个许可。有点类似限流的作用。叫信号灯的原因也和他的用处有关,比如某商场就5个停车位,每个停车位只能停一辆车,如果这个时候来了10辆车,必须要等前面有空的车位才能进入。

  • 使用案例

public static void main(String[] args) {
    Semaphore semaphore=new Semaphore(5);
    for(int i=0;i<10;i++){
        new Car(i,semaphore).start();
    }
}
static class Car extends Thread{
    private int num;
    private Semaphore semaphore;
    public Car(int num, Semaphore semaphore) {
        this.num = num;
        this.semaphore = semaphore;
    }

    public void run(){
        try {
            semaphore.acquire(); //获取一个许可
            System.out.println("第"+num+"占用一个停车位");
            TimeUnit.SECONDS.sleep(2);
            System.out.println("第"+num+"辆车走喽");
            semaphore.release(); //释放一个许可
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • 使用场景

可以实现对某些接口访问的限流。

  • 源码分析

Semaphore也是基于AQS来实现的,内部使用state表示许可数量;它的实现方式和CountDownLatch的差异点在于acquire -> acquireSharedInterruptibly -> tryAcquireShared方法的实现,这个方法是在Semaphore方法中重写的,存在公平与非公平两种实现。

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

Semaphore - > NonfairSync - > tryAcquireShared - > nonfairTryAcquireShared

非公平情况:

        // 非公平情况
        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }

        // 自旋去获得一个许可,如果许可获取失败,也就是remaining<0的情况下,让当前线程阻塞
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

Semaphore - > FairSync - > tryAcquireShared 

公平情况:

        //公平情况
        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;
            }
        }

        public final boolean hasQueuedPredecessors() {
            // The correctness of this depends on head being initialized
            // before tail and on head.next being accurate if the current
            // thread is first in queue.
            Node t = tail; // Read fields in reverse initialization order
            Node h = head;
            Node s;
            return h != t &&
                ((s = h.next) == null || s.thread != Thread.currentThread());
        }

Semaphore - > release - > releaseShared

releaseShared方法的逻辑也很简单,就是通过线程安全的方式去增加一个许可,如果增加成功,则触发释放一个共享锁,也就是让之前处于阻塞的线程重新运行。

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

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

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

 

你可能感兴趣的:(Java多线程)