一、Semaphore
Semaphore管理的是一组许可证,acquire方法将获取一个许可证,如果池中还有许可证,则获取成功并持有该许可证,否则阻塞。release方法向池中添加一个许可证,释放成功,acquire方法阻塞的线程就会唤醒并获得许可证。实际上许可证并不特指某些对象,而是由一个整形数字扮演许可证的角色。
内部类:
1、sync:队列
abstract static class Sync extends AbstractQueuedSynchronizer {
继承AQS,构建等待队列
2、nonfairSync:非公平锁
static final class NonfairSync extends Sync {
3、FairSync:公平锁
static final class FairSync extends Sync {
二、应用
常用来限制线程访问共享资源的数量,做流量分流。
实例代码:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(8);
final Semaphore semaphore = new Semaphore(5);
final CountDownLatch countDownLatch = new CountDownLatch(1);
for (int i = 1; i <= 8; i++) {
final int index = i;
service.submit(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("当前线程:" + Thread.currentThread().getName() + " 获得许可" );
//不释放锁,模拟业务操作
Thread.sleep(1000);
System.out.println("当前线程:" + Thread.currentThread().getName() + "释放许可");
//System.out.println("当前允许进入的最大任务数:" + semaphore.availablePermits());
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
if(index == 8){
countDownLatch.countDown();
}
}
}
});
}
try {
System.out.println("main线程等待:" + Thread.currentThread().getName());
countDownLatch.await();
System.out.println("模拟执行完毕.....");
System.out.println("关闭线程池");
service.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
当前线程:pool-1-thread-1 获得许可
当前线程:pool-1-thread-4 获得许可
当前线程:pool-1-thread-3 获得许可
当前线程:pool-1-thread-5 获得许可
当前线程:pool-1-thread-2 获得许可
main线程等待:main
当前线程:pool-1-thread-1释放许可
当前线程:pool-1-thread-2释放许可
当前线程:pool-1-thread-3释放许可
当前线程:pool-1-thread-6 获得许可
当前线程:pool-1-thread-7 获得许可
当前线程:pool-1-thread-8 获得许可
当前线程:pool-1-thread-4释放许可
当前线程:pool-1-thread-5释放许可
当前线程:pool-1-thread-6释放许可
当前线程:pool-1-thread-8释放许可
模拟执行完毕.....
关闭线程池
当前线程:pool-1-thread-7释放许可
许可就等待,否则执行任务处理。
三、实现原理
1、acquire实现:
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
acquires值默认为1,表示尝试获取1个许可,remaining代表剩余的许可数。
- 如果remaining < 0,表示目前没有剩余的许可。
- 当前线程进入AQS中的doAcquireSharedInterruptibly方法等待可用许可并挂起,直到被唤醒。
2、 release实现
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;
}
}
releases值默认为1,表示尝试释放1个许可,next 代表如果许可释放成功,可用许可的数量。
- 通过unsafe.compareAndSwapInt修改state的值,确保同一时刻只有一个线程可以释放成功。
- 许可释放成功,当前线程进入到AQS的doReleaseShared方法,唤醒队列中等待许可的线程。
3、公平acquire实现:
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;
}
}
acquires值默认为1,表示尝试获取1个许可,remaining代表剩余的许可数。
可以看到和非公平策略相比,就多了一个对阻塞队列的检查。
- 如果阻塞队列没有等待的线程,则参与许可的竞争。
- 否则直接插入到阻塞队列尾节点并挂起,等待被唤醒。