Semaphore是计数信号量,管理一系列许可证。
线程通过acquire方法获取许可证,成功则许可证总数减一并执行任务,反之阻塞等待;
线程通过release方法释放许可证,许可证总数加一。
// 默认非公平模式
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
// 可设置公平或非公平
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
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);
}
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
// 总许可证数
int available = getState();
// 计算剩余许可证数 = 总许可证数 - 被当前线程拿走的许可证数
int remaining = available - acquires;
// 剩余的必须大于零,然后更新总许可证数为剩余许可证数
if (remaining < 0 || compareAndSetState(available, remaining))
return remaining;
}
}
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) {
super(permits);
}
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;
}
}
}
非公平与公平获取许可证的不同在于:
非公平获取许可证时,不会查看等待队列,无法保证线程获取许可证的顺序,插队是允许;
公平模式则能保证获取许可的顺序为线程调用acquire方法的顺序–FIFO(先进先出)
public static void demo01() throws InterruptedException {
Semaphore semaphore = new Semaphore(1);
Thread t1 = new Thread(() -> method01(semaphore), "t1");
Thread t2 = new Thread(() -> method01(semaphore), "t2");
Thread t3 = new Thread(() -> method01(semaphore), "t3");
t1.start();
t2.start();
t3.start();
}
private static void method01(Semaphore semaphore) {
String thName = Thread.currentThread().getName();
try {
long start = System.currentTimeMillis();
Thread.sleep((long) (Math.random() * 1000));
System.out.println("线程: " + thName + "执行acquire方法耗时:" + (System.currentTimeMillis() - start));
Thread.sleep(1000);
semaphore.acquire();
System.out.println("线程: " + thName + "获得许可");
long start2 = System.currentTimeMillis();
Thread.sleep((long) (Math.random() * 3000));
System.out.println("线程: " + thName + "任务耗时:" + (System.currentTimeMillis() - start2));
Thread.sleep(1000);
System.err.println("线程: " + thName + "释放许可");
semaphore.release();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void demo02() throws InterruptedException {
Semaphore semaphore = new Semaphore(2);
Thread t1 = new Thread(() -> method01(semaphore), "t1");
Thread t2 = new Thread(() -> method01(semaphore), "t2");
Thread t3 = new Thread(() -> method01(semaphore), "t3");
Thread t4 = new Thread(() -> method01(semaphore), "t4");
Thread t5 = new Thread(() -> method01(semaphore), "t5");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
public static void demo02() throws InterruptedException {
Semaphore semaphore = new Semaphore(2);
Thread t1 = new Thread(() -> method02(semaphore), "t1");
Thread t2 = new Thread(() -> method02(semaphore), "t2");
Thread t3 = new Thread(() -> method02(semaphore), "t3");
Thread t4 = new Thread(() -> method02(semaphore), "t4");
Thread t5 = new Thread(() -> method02(semaphore), "t5");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
private static void method02(Semaphore semaphore) {
String thName = Thread.currentThread().getName();
try {
long start = System.currentTimeMillis();
Thread.sleep((long) (Math.random() * 1000));
System.out.println("线程: " + thName + "执行acquire方法耗时:" + (System.currentTimeMillis() - start));
Thread.sleep(1000);
semaphore.acquire(2);
System.out.println("线程: " + thName + "获得许可");
long start2 = System.currentTimeMillis();
Thread.sleep((long) (Math.random() * 3000));
System.out.println("线程: " + thName + "任务耗时:" + (System.currentTimeMillis() - start2));
Thread.sleep(1000);
System.err.println("线程: " + thName + "释放许可");
semaphore.release(2);
} catch (Exception e) {
e.printStackTrace();
}
}
acquire():获取许可证,失败则阻塞等待;
tryAcquire():尝试获取许可证,失败后不会阻塞,继续执行
tryAcquire(long timeout, TimeUnit unit):在超时时间内阻塞等待获取许可证,超时后唤醒继续执行;
public static void demo03() throws InterruptedException {
Semaphore semaphore = new Semaphore(1);
Thread t1 = new Thread(() -> method03_1(semaphore), "t1");
Thread t2 = new Thread(() -> method03_1(semaphore), "t2");
Thread t3 = new Thread(() -> method03_1(semaphore), "t3");
t1.start();
t2.start();
t3.start();
}
private static void method03_1(Semaphore semaphore) {
String thName = Thread.currentThread().getName();
try {
long start = System.currentTimeMillis();
Thread.sleep((long) (Math.random() * 1000));
System.out.println("线程: " + thName + "执行acquire方法耗时:" + (System.currentTimeMillis() - start));
Thread.sleep(1000);
while (true) {
if (semaphore.tryAcquire()) {
System.out.println("线程: " + thName + "获得许可");
long start2 = System.currentTimeMillis();
Thread.sleep((long) (Math.random() * 3000));
System.out.println("线程: " + thName + "任务耗时:" + (System.currentTimeMillis() - start2));
Thread.sleep(1000);
System.err.println("线程: " + thName + "释放许可");
semaphore.release();
break;
} else {
System.out.println("线程: " + thName + "获取许可失败");
Thread.sleep(1000);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static void method03_2(Semaphore semaphore) {
String thName = Thread.currentThread().getName();
try {
long start = System.currentTimeMillis();
Thread.sleep((long) (Math.random() * 1000));
System.out.println("线程: " + thName + "执行acquire方法耗时:" + (System.currentTimeMillis() - start));
Thread.sleep(1000);
if (semaphore.tryAcquire(5000, TimeUnit.MILLISECONDS)) {
System.out.println("线程: " + thName + "获得许可");
long start2 = System.currentTimeMillis();
Thread.sleep((long) (Math.random() * 3000));
System.out.println("线程: " + thName + "任务耗时:" + (System.currentTimeMillis() - start2));
Thread.sleep(1000);
System.err.println("线程: " + thName + "释放许可");
semaphore.release();
} else {
System.err.println("线程: " + thName + "超时了");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
持有的许可证,不是必须要当前线程释放,可由其他线程释放;
一个线程可获取释放多个许可证;