提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
作用
对访问某个资源的线程数量进行控制,若访问线程数量大于规定数量,后面来的全部阻塞。
例如:厕所一共只有10个坑位。。那么只能同时10个人用着,后面来的人都得等着(阻塞),如果走了2个人,那么又可以进去2个人
使用
Semaphore 维护最大访问线程数量为2,一个创建5个线程。只有2个能加锁执行,剩余3个等待。直到加锁线程释放锁 。
public class Main{
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2);
for(int i=0;i<5;i++){
new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"开始执行");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+"执行完毕");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},i+"").start();
}
}
}
1开始执行
0开始执行
0执行完毕
1执行完毕
3开始执行
2开始执行
2执行完毕
3执行完毕
4开始执行
4执行完毕
设计思想
基于aqs实现
(0)共享模式,进入同步代码块的线程可以有多个
(1)state表示当前还有几个许可证
(2)重写了AQS中tryAcquireShared()、tryReleaseShared(),根据Semaphore自身特性重新定义了竞争锁、释放锁的策略
自身特性如下:
state>0 表示许可证数量,此时线程将state-1,成功竞争锁
state<=0 表示阻塞线程数量,此时线程将state-1,自己阻塞。
(4)所有等待节点以共享模式阻塞,每次唤醒,唤醒全部阻塞结点
源码分析
Semaphore初始化
默认非公平锁,可设置
指定了许可线程数量,permits赋值给state
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
根据自身特性重写方法
acquires表示当前线程所要申请的许可证数量,默认为1。
将当前已有的许可证数量-线程申请的数量,如果结果<0 表示申请失败,该线程阻塞
非公平锁
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
公平锁,比非公平多了一个判断队列是否有人的机制
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;
}
}
释放锁资源,将state+1,表示许可证数量+1
public void release() {
sync.releaseShared(1);
}
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;
}
}
线程调用acquire()加锁
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
tryAcquireShared(arg) < 0 表示许可证数量<0 ,此时线程排队
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
线程调用release()解锁,释放许可证成功后,立即唤醒阻塞线程
public void release() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}