一、前瞻
并发编程源码解析(一)ReentrantLock源码解析(超详细)-CSDN博客
并发编程源码解析(二)ReentrantReadWriteLock源码解析之一写锁-CSDN博客
并发编程源码解析(三)ReentrantReadWriteLock源码解析之一写锁-CSDN博客
并发编程源码解析(四)ConcurrentHashMap源码解析之一基础概念介绍以及散列算法讲解-CSDN博客
并发编程源码解析(五)ConcurrentHashMap源码解析之二读与写源码分析-CSDN博客
并发编程源码解析(六)ConcurrentHashMap源码解析之三并发扩容
并发编程源码解析(七)CountDownLatch 源码解析-CSDN博客
二、Semaphore 的基本介绍
Semaphore 通常是一个计数器,用于跟踪共享资源的使用情况。当进程想要使用共享资源时,它会尝试获取 Semaphore,如果 Semaphore 的计数器为零,表示资源正在使用中,那么进程将被阻塞,直到 Semaphore 可用。
它与 CountDownLatch 的作用区别主要在,CountDownLatch 的计数是不可回收的,而Semaphore 是可回收的;正是因为这一特性他也被广泛的应用于限流的场景当中。
三、Semaphore 的 使用
semaphore.acquire() 时信号量会将state -1;
semaphore.release() 时信号量会将state +1;
当为state == 0 是 acquire方法会被阻塞住
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class ThreadPool {
private static final int MAX_THREADS = 10;
private ExecutorService executor;
private Semaphore semaphore;
public ThreadPool() {
executor = Executors.newFixedThreadPool(MAX_THREADS);
semaphore = new Semaphore(MAX_THREADS);
}
public void submit(Runnable task) {
try {
semaphore.acquire();
executor.submit(() -> {
try {
task.run();
} finally {
semaphore.release();
}
});
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
四、 Semaphore 源码解析
4.1 构造方法
主要含有两个参数,一个是限制次数的,一个是控制公平锁和非公平锁;默认情况下会走非公平锁哦。
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
4.2 acquire方法
acquire 方法调用 和 CountDownLatch 是一致的所以这篇文章当中就不重复赘述了,介绍tryAcyquire 方法 公平锁与非公平锁之间的区别。
public void acquire() throws InterruptedException {
//与CountDownLatch的await方法一致的
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
//上一期CountDownLatch已经讲过了,我们直接来讲解tryAcquireShared的方法
doAcquireSharedInterruptibly(arg);
}
4.2.1 公平锁
// 整个方法和ReentrantLock十分的类似
protected int tryAcquireShared(int acquires) {
//死循环
for (;;) {
//检查队列当中是否有在排队的线程
if (hasQueuedPredecessors())
return -1;
//获取state
int available = getState();
//减小次数
int remaining = available - acquires;
// 如果小于0 或者 CAS 加锁成功则返回剩余次数
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
4.2.2 非公平锁
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
// 和公平锁一模一样只是少了一个检查队列的部分
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
公平锁和非公平锁十分相似,只是公平锁多了一个检查队列中是否含有线程这一方法,来保证锁的公平性;他们与之间ReentrantLock的公平锁与非公平锁的逻辑也十分相似。
4.3 release方法
这块逻辑比较简单,除了验证参数之外,就是单纯的使用CAS去变更了state的大小就没有其他操作了。
public void release() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
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");
//CAS 尝试变更状态;直到返回成功为止
if (compareAndSetState(current, next))
return true;
}
}