JUC
Semaphore
Sempahore原理
类图:
Sempahore的构造方法有两个
public Semaphore(int permits) {
//默认非公平的
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
实例代码:
//创建Semaphore对象
Semaphore semaphore = new Semaphore(3);
for(int i=0;i<5;i++){
new Thread(()->{
try {
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
log.info("running...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("end...");
} finally {
semaphore.release();
}
}).start();
}
源码:
1.调用Semaphore semaphore = new Semaphore(3); 实际是调用了public Semaphore(int permits) {
//默认非公平的
sync = new NonfairSync(permits);
}构造方法,在NonfairSync(int permits)中,是调用了父类Sync的Sync(int permits)构造方法,
Sync(int permits) {
setState(permits);
}
最后将permits赋值给Sync的state属性,state为AbstractQueuedSynchronizer(简称AQS)的private volatile int state;
2.Sempahore.acqure()解锁过程方法调用链
- 1.acquire() -> sync.acquireSharedInterruptibly(1)->AQS.acquireSharedInterruptibly(1);
- 2.AQS.acquireSharedInterruptibly(1) ->NonfairSync.tryAcquireShared(int acquires)
- 3.NonfairSync.tryAcquireShared(int acquires) -> Sync.nonfairTryAcquireShared(int acquires)
- 4.Sync.nonfairTryAcquireShared(int acquires) 返回值大于等于0表示获取资源成功
- 5.Sync.nonfairTryAcquireShared(int acquires) 返回值小于0表示获取资源失败,返回到AQS.acquireSharedInterruptibly(1)
- 6.AQS.acquireSharedInterruptibly(1) ->AQS. doAcquireSharedInterruptibly(int arg)
- 7.AQS. doAcquireSharedInterruptibly(int arg)先创建一个队列节点Node(Node的状态为Shared),将Node加入到等待队列中(此时线程还在执行,还未阻塞),获取Node节点的前置节点,判断前置节点是否是头节点,如果是头节点则再次尝试调用NonfairSync.tryAcquireShared(int acquires)获取资源,此时如果获取成功则将前置节点从队列中删除,Node作为新的前置节点并唤醒后续为SHARED节点;如果不是头节点或者尝试获取资源失败,则调用AQS.shouldParkAfterFailedAcquire()的for (;😉 循环逻辑。,第一次调用会将判断前置是否是头节点,是头节点则将头节点的状态waitStatus改为-1并且返回false,然后再经历一遍上述操作(如果是头节点的后继节点会再次调用NonfairSync.tryAcquireShared(int acquires)获取资源)的for (;😉 循环逻辑。如果还未获取到锁,则再次致谢AQS.shouldParkAfterFailedAcquire(),AQS.shouldParkAfterFailedAcquire()返回为true,进入另一个条件判断parkAndCheckInterrupt(),在parkAndCheckInterrupt()会调用LockSupport.park()将线程阻塞。
- 总结:第一个要进入等待队列的节点线程,一共会尝试三次调用NonfairSync.tryAcquireShared(int acquires)来获取资源
源码:
public class Semaphore implements java.io.Serializable {
public void acquire() throws InterruptedException {
//sync没有重写AQS的acquireSharedInterruptibly
//这里直接调用AQS的acquireSharedInterruptibly方法
sync.acquireSharedInterruptibly(1);
}
abstract static class Sync extends AbstractQueuedSynchronizer {
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
}
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L;
NonfairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
}
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
//获取前置节点
final Node p = node.predecessor();
if (p == head) {
//队列的头结点后的首个节点(队列中的第二个),再次尝试获取资源
int r = tryAcquireShared(arg);
//如果r>=0 表示获取成功,则进入成功流程
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
//失败
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
/**
* 生成等待队列的节点对象
*/
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
}
1.加解锁的流程
加锁过程
Sempahore有点像停车场,permits就好像停车场的车位数量,当线程获得了permits就像是获得了停车位,然后停车场显示空余车位减一。
刚开始设置,permits(state)为3,这是有5个线程来获取资源
假设其中的Thread-1,Thread-2,Thread-4 cas竞争成功,而Thread-0和Thread-3竞争失败,进入AQS队列park阻塞。
其中Thread-1,Thread-2,Thread-4 在Sync.nonfairTryAcquireShared()方法中,调用cas都成功了,成功获取到了资源。
而Thread-0在Sync.nonfairTryAcquireShared()方法中返回失败。导致进入流程doAcquireSharedInterruptibly(arg);的for (;😉 循环逻辑。
- 先添加节点到等待队列,判断是否前置节点为头节点(此时确实是头节点)。
- thread-0的前置节点为头节点,所以调用NonfairSync.tryAcquireShared(int acquires)再次尝试获取资源,由于Thread-1,Thread-2,Thread-4 还未执行完所以获取失败。
- 则进入AQS.shouldParkAfterFailedAcquire()方法,方法中判断前置节点的状态waitStatus,此时waitStatus为0,则调用compareAndSetWaitStatus(pred, ws, Node.SIGNAL);将前置节点的waitStatus改为-1并且返回false。
- 返回false后再次执行循环,进行上述操作。
- 由于Node(thread-0)还是头节点的后继节点,所以再次进行调用NonfairSync.tryAcquireShared(int acquires)再次尝试获取资源。由于Thread-1,Thread-2,Thread-4 还未执行完所以获取失败,所以还是失败。
- 再次调用AQS.shouldParkAfterFailedAcquire()方法,此时头节点状态为-1,AQS.shouldParkAfterFailedAcquire()方法返回true,
- 然后进入if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())语句的后一个条件判断,调用parkAndCheckInterrupt(),
- 在parkAndCheckInterrupt()方法内调用LockSupport.park()阻塞thread-0线程
Thread-3同样的在Sync.nonfairTryAcquireShared()方法中返回失败。导致进入流程doAcquireSharedInterruptibly(arg);的for (;😉 循环逻辑。
- 先添加节点到等待队列,判断是否前置节点为头节点(此时前置节点不为头节点,而是Node(thread-0))。
- 直接进入AQS.shouldParkAfterFailedAcquire()方法,方法中判断前置节点的状态waitStatus,此时Node(thread-0))的waitStatus为0,则调用compareAndSetWaitStatus(pred, ws, Node.SIGNAL);将前置节点Node(thread-0))的waitStatus改为-1并且返回false。
- 返回false后再次执行循环,进行上述操作。
- 由于前置节点还是Node(thread-0)并不是头节点,所以再次调用AQS.shouldParkAfterFailedAcquire()方法,此时Node(thread-0)节点的状态waitStatus为-1,AQS.shouldParkAfterFailedAcquire()方法返回true,
- 然后进入if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())语句的后一个条件判断,调用parkAndCheckInterrupt(),
- 在parkAndCheckInterrupt()方法内调用LockSupport.park()阻塞thread-0线程
3.Sempahore.release()解锁过程方法调用链:
- Semaphore.release()->Sync.releaseShared(1)->AQS.releaseShared(1)
- AQS.releaseShared(1)->Sync.tryReleaseShared(1)
- Sync.tryReleaseShared(1)失败则表示解锁失败
- Sync.tryReleaseShared(1)成功则AQS.releaseShared(1)调用AQS. doReleaseShared()
- AQS. doReleaseShared()方法中获取头节点head,判断头节点是否为空,为空则不需要唤醒;不为空则判断头节点状态是否为-1如果为-1,则调用cas将头节点状态改为0,并且唤醒头节点的后继节点。另外如果头节点状态为0则将头节点状态改为PROPAGATE
public class Semaphore implements java.io.Serializable {
public void release() {
sync.releaseShared(1);
}
abstract static class Sync extends AbstractQueuedSynchronizer {
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;
}
}
}
}
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
//判断前置节点的状态是否为-1(Node.SIGNAL)
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
//尝试设置失败重新循环再次cas 直至成功
continue; // loop to recheck cases
//唤醒后继节点
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
}
解锁过程:
此时Thread-4执行完毕调用release()方法释放了permits,状态如下:
Thread-4调用了release()方法将头节点后继节点唤醒,这里唤醒Node(Thread-0)。
-
Thread-0在parkAndCheckInterrupt()方法的LockSupport.park(this);代码处被唤醒,继续执行return Thread.interrupted();
-
private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
-
返回到AQS.doAcquireSharedInterruptibly(int arg)方法的if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())逻辑判断出 -
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException();
-
此时如果thread-0没有被其他线程调用interrupt()方法(如果已被调用intterupt方法则thread-0抛出异常InterruptedException),则parkAndCheckInterrupt()返回false。
-
thread-0再次进入AQS.doAcquireSharedInterruptibly(int arg)的for(;😉,调用NonfairSync.tryAcquireShared(int acquires)再次尝试获取资源,此时没有其他线程竞争资源,thread-0获取资源成功,调用int r = tryAcquireShared(arg)返回0。
-
然后调用setHeadAndPropagate(node, r);将Node(Thread-0)设置为头节点,并调用Node的thread属性设置为null,将原来的头节点从队列中删除
-
判断if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) ,判断是否继续唤醒后继节点,由于h.waitStatus 为-1进入逻辑块
-
if (s == null || s.isShared())判断后继节点是否为SHREAED,此时的后继节点为Node(Thread-3)SHARED,所以调用doReleaseShared();唤醒Node(Thread-3)
-
此时Thread-3被唤醒,但是Thread-3再调用NonfairSync.tryAcquireShared(int acquires)尝试获取资源时失败(已经没有资源),所以Thread-3会再次调用LockSupport.park()方法进入阻塞
接下来Thread-0竞争成功,permits再次设置为0,设置自己为head节点,断开原来的head节点,unpark接下来的Thread-3节点,但由于permits是0,因此Thread-3在调用NonfairSync.tryAcquireShared(int acquires)获取资源尝试不成功后再次进入park状态。