关于java:通俗易懂的JUC源码剖析Semaphore

47次阅读

共计 2224 个字符,预计需要花费 6 分钟才能阅读完成。

前言

Semaphore 意为信号量,它用来限度某段时间内的最大并发资源数。例如数据库连接池,停车位等。上面通过停车位的栗子来阐明 Semaphore 的应用形式。

import java.util.concurrent.Semaphore;
public class SemaphoreDemo {private static Semaphore semaphore = new Semaphore(10);
    public static void main(String[] args) {for (int i = 1; i <= 50; i++) {new Thread(() -> {
                try {if (semaphore.availablePermits() == 0) {System.out.println("车位已满,须要期待...");
                    }
                    semaphore.acquire();
                    System.out.println("有空余车位,驶进停车场");
                    // 模仿在停车场 smoke or something
                    Thread.sleep(3000);
                    System.out.println("老婆喊我回家吃饭,驶出停车场");
                    semaphore.release();} catch (InterruptedException e) {// ignored}
            }).start();}
    }
}

实现原理

看一眼 Semaphore 的类构造,外部类继承了 AQS,同时提供了偏心和非偏心策略。

咱们能够在构造函数中指定是偏心还是非偏心,默认是非偏心策略。

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

再来看重要办法(以 NonfairSync 为例剖析):
acquire()

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

能够看到,调用了 AQS 的模板办法,acquireSharedInterruptibly 外面会调用子类重写的 tryAcquireShared,来看看相干逻辑:

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);
}
final int nonfairTryAcquireShared(int acquires) {
    // CAS + 自璇
    for (;;) {
        // 获取以后残余资源数
        int available = getState();
        // 计算获取 acquires 个资源后,残余资源数
        int remaining = available - acquires;
        // 如果不够用或者够用并且 CAS 设置残余数胜利,则返回
        // 否则循环重试 CAS 操作
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
 }
}

release()

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

同样,release 调用了 AQS 的模板办法,releaseShared 外面会调用子类重写的 tryReleaseShared 办法,来看看子类具体实现逻辑:

protected final boolean tryReleaseShared(int releases) {
    // CAS + 自璇
    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;
        }
}

代码逻辑也很简略,不做赘述。

FairSync 偏心式的获取,就是在 tryAcquireShared 时先判断队列中有无在期待的元素,有的话就返回 -1,进入同步队列阻塞获取。相干代码如下:

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

参考资料:
《Java 并发编程之美》

正文完
 0