用途
设置一定数量的资源,只有获取到资源的线程才会执行下去,否则会一直等待,和ReentrantLock不同的是,ReentrantLock只允许一个线程获取资源,而Semaphore允许多个线程获取到资源
DEMO
package com.skindow.sql;
import java.util.concurrent.Semaphore;
/**
* @ Author :syc.
* @ Date :Created in 16:03 2021/3/12
* @ Description:
* @ Modified By:
* @ Version:
*/
public class SemaphoreTest {
public static final int COUNT = 1;
public static final Semaphore SEMAPHORE = new Semaphore(COUNT);
public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
SemaphoreTest.myThread myThread = new SemaphoreTest.myThread(i);
myThread.setName("test_" + i);
myThread.start();
}
}
private static class myThread extends Thread {
public Integer i;
public myThread(Integer i) {
this.i = i;
}
@Override
public void run() {
try {
boolean flag = false;
int j = 0;
System.out.println(Thread.currentThread().getName() + " 获取共享锁中...");
SEMAPHORE.acquire();
System.out.println(Thread.currentThread().getName() + " 已获得共享锁,还剩" + SEMAPHORE.availablePermits() + "个共享锁");
for (; ; ) {
if (getCount() > 0 && flag) {
System.out.println(Thread.currentThread().getName() + " 获取共享锁中...");
SEMAPHORE.acquire();
System.out.println(Thread.currentThread().getName() + " 已获得共享锁,还剩" + SEMAPHORE.availablePermits() + "个共享锁");
}
flag = true;
j++;
System.out.println(Thread.currentThread().getName() + " RUNNING");
Thread.sleep(5000);
if (j == 2) {
SEMAPHORE.release();
System.out.println(Thread.currentThread().getName() + " 释放当前共享锁...");
Thread.sleep(1000);
continue;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static synchronized int getCount() {
return SEMAPHORE.availablePermits();
}
}
结构图
构造方法
//初始化一个非公平同步阻塞队列,permits为共享锁数量
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
//根据fair来判断是初始化公平锁还是非公平锁同步阻塞队列
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
acquire() 方法说明
以下Semaphore acquire() 和CountDownLatch await() 方法类似,可看上期解读
CountDownLatch源码分析,不同点在于判断共享资源逻辑上,在CountDownLatch中,如果还有共享资源,则将当前线程挂起,并加入同步阻塞队列中等待唤醒,而Semaphore则相反,如果没有共享资源可用,则挂起当前线程,等其他线程将共享资源释放在将其唤醒。从这里不同点就可以看出两个共享资源锁的区别了
这里简单说下acquire流程
有中断就抛出中断异常,判断当前信号量是否大于0,也就是可共享资源尚有余否,大于0时,则分配一个共享资源(减一)给当前线程,否则就将当前线程挂起,等待其他线程释放共享资源唤醒其争夺余下的共享资源
Semaphore acquire() 方法
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
AbstractQueuedSynchronizer acquireSharedInterruptibly(int arg) 方法
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
AbstractQueuedSynchronizer doAcquireSharedInterruptibly(int 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);
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);
}
}
Semaphore release()方法
public void release() {
sync.releaseShared(1);
}
AbstractQueuedSynchronizer releaseShared(int arg) 方法
public final boolean releaseShared(int arg) {
//释放当前资源
if (tryReleaseShared(arg)) {
//如果资源成功,唤醒队列节点线程
doReleaseShared();
return true;
}
return false;
}
release方法总结
- 释放当前共享资源
- 释放成功,则唤醒同步阻塞队列中满足条件的线程(doReleaseShared 在上期CountDownLatch就介绍过了,这里就不过多解释了)