示例代码
public class TestSemaphore {
public static Semaphore semaphore=new Semaphore(2);
public static void main(String[] args) {
System.out.println("main start");
for (int i =0;i<5;i++){
new Thread(new Taskes()).start();
}
System.out.println("main end");
}
}
class Taskes implements Runnable{
@Override
public void run() {
try {
TestSemaphore.semaphore.acquire();
LocalTime time = LocalTime.now();
System.out.println(Thread.currentThread().getName()+"任务开始--------"+time.getMinute()+":"+time.getSecond());
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName()+"任务结束");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
TestSemaphore.semaphore.release();
}
}
}

我们看到,一个开了5个线程,但每次只有两个在执行
源码

从它的内部类可以看出跟lock师出同门都是基于AQS的
Sync(int permits) {
setState(permits);
}
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void release() {
sync.releaseShared(1);
}
我们能够看出,构造器设置state值,其实就是加了permits把共享锁,每次调用acquire就是在获取共享锁,调用release是释放共享锁。
与其他加锁不同的是,这里获取锁是state的自减操作,当state归零时便无法获取锁只能阻塞;
释放锁是state的自加操作
这种变化情况与平时的加锁state变化情况正好相反。
acquire
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//tryAcquireShared看下面的nonfairTryAcquireShared即可
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
final int nonfairTryAcquireShared(int acquires) {
//自旋获取,state自减后的值返回,当state剩余量小于0时会进入doAcquireSharedInterruptibly中进行阻塞获取;否则会在acquireSharedInterruptibly中返回
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
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);
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);
}
}
大概总结一下就是
- 我们这里默认获取量为1,每次获取的时候会判断剩余量并进行自减操作,将剩余量返回,如果令牌数仍大于0,会直接返回,不影响。
- 对于令牌数小于0了,就将当前线程放入AQS同步队列中让其阻塞
release
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");
if (compareAndSetState(current, next))
return true;
}
}
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
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;
}
}
总结一下:
- 线程将令牌还回去时,会将还回去的令牌数量增加,这个增加用CAS进行,成功返回
True - 返回为
True时会在AQS中对同步队列中的节点进行唤醒操作
来源:CSDN
作者:Mutou_ren
链接:https://blog.csdn.net/Mutou_ren/article/details/103832797