文章中的内容不保证完全正确,如果有误可以给我留言
Semphore的使用示例
基于AQS的共享模式实现
import java.util.concurrent.Semaphore;
public class SemaphoreSample {
public static void main(String[] args) {
Semaphore semaphore=new Semaphore(2);
for(int i=0;i<5;i++){
new Thread(new Task(semaphore,"Thread"+i)).start();
}
}
static class Task extends Thread{
Semaphore semaphore;
public Task(Semaphore semaphore,String tName){
this.semaphore=semaphore;
this.setName(tName);
}
public void run(){
try{
semaphore.acquire();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+":acquire() at time:"+System.currentTimeMillis());
Thread.sleep(1000);
semaphore.release();
System.out.println(Thread.currentThread().getName()+":release() at time:"+System.currentTimeMillis());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Semaphore的实现原理
Semaphore基于共享锁实现,在构造时,会输入一个参数permits,表示最大的共享数量。可以这样比喻,把Semaphore比作电影院的门禁,初始时设置有最大容量permits,进去一个人,permits就减一(减去的数也可以大于1),当permits为0时,再想进去只能在外面排队。当里面的人出来后,permits会加上这个人进去时减的数,这是permits不为0了,然后会通知排队的人进去,直到permits再次为0.
Semaphore的构造函数
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
acquire()
/**
* 获得一个许可,如果一个许可被提供那么立即返回并将可提供的许可数量减一
* 如果没有许可可以提供,出于线程调度的目的,当前线程被停用并处于休眠状
* 态直到下面两种情况发生:
* 其他线程为此信号量调用release方法,当前线程将被下一个分配许可
* 其他一些线程{@linkplain Thread#interrupt interrupts}当前线程
*/
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
acquireSharedInterruptibly()
/**
* 以共享模式获取,如果中断则终止,通过首先检查中断状态然后至少调用一次
* tryAcquireShared方法,成功就返回。否则线程将排队,并可能反复阻塞和
* 解除阻塞,并调用tryAcquireShared,直到成功或线程被中断为止
* @throws InterruptedException if the current thread is interrupted
*/
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
tryAcquireShared()
在AbstractQueuedSynchronizer类中实现了默认处理方法
//返回值:剩下的许可的数量
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
在Semaphore的内部类NonfairSync和FairSync中均有实现,因为上面示例中的是使用的非公平锁,在这里展示NonfairSync中的实现
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
nonfairTryAcquireShared()
在Semaphore的内部类Sync中实现
final int nonfairTryAcquireShared(int acquires) {
for (;;) {//在高并发场景下,compareAndSetState()方法可能会失败,所以进行自旋不停的尝试
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))//使用CAS替换掉原来state的值
return remaining;
}
}
doAcquireSharedInterruptibly()
/**
* Acquires in shared interruptible mode.
* @param arg the acquire argument
*/
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);//将当前线程加入队列
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);//再次尝试获取资源
if (r >= 0) {//获取成功
setHeadAndPropagate(node, r);
p.next = null; // help GC
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&//检查获取失败后是否因该阻塞
parkAndCheckInterrupt())//阻塞当前线程,暂停循环,直到被唤醒
throw new InterruptedException();
}
} catch (Throwable t) {
cancelAcquire(node);
throw t;
}
}
release()
不带参数时默认释放一个许可,带参数时可以一次释放多个许可
public void release() {
sync.releaseShared(1);
}
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
releaseShared()
public final boolean releaseShared(int arg) {
//先尝试释放许可,释放成功后唤醒阻塞的线程重新争夺许可
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
tryReleaseShared()
在AbstractQueuedSynchronizer中实现了默认版本
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
在Semaphore的内部类Sync(继承自AbstractQueuedSynchronizer)的版本
//释放许可,即设置state=state+releases,设置需为原子操作,可能会有多个线程同时释放许可
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;
}
}
doReleaseShared()
/**
* 把当前结点设置为SIGNAL或者PROPAGATE
* 唤醒head.next(B节点),B节点唤醒后可以竞争锁,成功后head->B,然后又会唤醒B.next,一直重复直到共享节点都唤醒
* head节点状态为SIGNAL,重置head.waitStatus->0,唤醒head节点线程,唤醒后线程去竞争共享锁
* head节点状态为0,将head.waitStatus->Node.PROPAGATE传播状态,表示需要将状态向后继节点传播
*/
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
//需要控制并发,因为入口有setHeadAndPropagate跟release两个,避免两次unpark
if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
continue; // loop to recheck cases
/** head状态为SIGNAL,且成功设置为0之后,唤醒head.next节点线程
* 此时head、head.next的线程都唤醒了,head.next会去竞争锁,成功后head会指向获取锁的节点,
* 也就是head发生了变化。看最底下一行代码可知,head发生变化后会重新循环,继续唤醒head的下一个节点
*/
unparkSuccessor(h);//唤醒线程后在doAcquireSharedInterruptibly方法中使用parkAndCheckInterrupt())方法阻塞的线程会重新执行doAcquireSharedInterruptibly中的循环
}
else if (ws == 0 &&
!h.compareAndSetWaitStatus(0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
unparkSuccessor()
从队列中唤醒线程
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
node.compareAndSetWaitStatus(ws, 0);
//若后继结点为空,或状态为CANCEL(已失效),则从后尾部往前遍历找到
//最前的一个处于正常阻塞状态的结点进行唤醒
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node p = tail; p != node && p != null; p = p.prev)
if (p.waitStatus <= 0)
s = p;
}
if (s != null)
LockSupport.unpark(s.thread);
}