Semaphore(信号量)是 JUC 中提供的另一个同步工具类,用于控制同时访问某个资源的线程数量。它可以用于限制同时执行的线程数量,从而实现资源的有序访问。Semaphore 的基本思想是维护一定数量的许可证(permits),每当一个线程访问资源时,会消耗一个许可证,当许可证用尽时,其他线程需要等待。Semaphore 提供了一种灵活的机制,适用于各种并发控制场景。
Semaphore 的使用
Semaphore 的构造器有两种形式:
- 使用初始许可证数量创建 Semaphore 实例:
java
public Semaphore(int permits);- 使用初始许可证数量和公平性标志创建 Semaphore 实例:
java
public Semaphore(int permits, boolean fair);Semaphore 的方法如下:
- acquire():尝试获取一个许可证,如果许可证用尽,则阻塞等待。
- acquire(int permits):尝试获取指定数量的许可证,如果许可证不足,则阻塞等待。
- release():释放一个许可证,将可用许可证的数量增加一个。如果任何线程正在尝试获取许可证,则会选择一个线程并授予刚刚发布的许可证。为了线程调度的目的,该线程被(重新)启用。
- release(int permits):释放指定数量的许可证。
- tryAcquire():尝试获取一个许可证,如果许可证用尽,返回 false,不阻塞等待。
- tryAcquire(int permits):尝试获取指定数量的许可证,如果许可证不足,返回 false,不阻塞等待。
使用 Semaphore 模拟停车场停车
java
package com.fly;
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
// 创建Semaphore实例,其许可证数量为5
private static final Semaphore semaphore = new Semaphore(5);
public static void main(String[] args) throws InterruptedException {
// 创建20个线程
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
// 尝试获取一个许可证,如果许可证用尽,则阻塞等待
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "停车成功!");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "驶出停车场!");
// 释放一个许可(增加一个许可)
semaphore.release();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
}
}
}在上面中使用 Semaphore 构造函数初始化了一个许可证为 5 的 Semaphore 实例,for 循环中创建了 10 个线程,线程中调用了 semaphore.acquire()会尝试获取一个许可证,因此会先打印 5 次停车。semaphore.acquire()执行 5 次后许可证为 0,此时会阻塞后续线程,等待车辆驶出停车场。由于打印车辆驶出后会调用 semaphore.release()释放一个许可证(增加一个许可证),因此其他线程会被唤醒,执行结果如下:
txt
Thread-2停车成功!
Thread-1停车成功!
Thread-3停车成功!
Thread-4停车成功!
Thread-0停车成功!
Thread-0驶出停车场!
Thread-3驶出停车场!
Thread-2驶出停车场!
Thread-6停车成功!
Thread-7停车成功!
Thread-4驶出停车场!
Thread-1驶出停车场!
Thread-5停车成功!
Thread-8停车成功!
Thread-9停车成功!
Thread-6驶出停车场!
Thread-5驶出停车场!
Thread-7驶出停车场!
Thread-9驶出停车场!
Thread-8驶出停车场!Semaphore 模拟一个线程等待多线程执行完成
java
package com.fly;
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
// 创建Semaphore实例,其许可证数量为3
private static final Semaphore semaphore = new Semaphore(0);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 5; i++) {
new Thread(()->{
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"执行完毕!");
// 释放一个许可证,将可用许可证的数量增加一个,如何 许可证>0 则等待的线程将被唤醒执行
semaphore.release();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
}
// 尝试获取一个许可证,由于Semaphore的许可证为0,因此会阻塞等待
semaphore.acquire();
System.out.println("主线程执行完毕");
}
}txt
Thread-0执行完毕!
Thread-4执行完毕!
Thread-1执行完毕!
Thread-2执行完毕!
Thread-3执行完毕!
主线程执行完毕Semaphore 模拟发令枪
Semaphore 实现发令枪,模拟多个线程在某一时刻同时开始执行:
java
package com.fly;
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
// 创建Semaphore实例,其许可证数量为0
private static final Semaphore semaphore = new Semaphore(0);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
// 尝试获取一个许可证,如果许可证用尽,则阻塞等待
semaphore.acquire();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "执行完毕!");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
}
// 模拟主线程执行
Thread.sleep(3000);
System.out.println("主线程执行完毕");
// 释放5个许可证,可用许可证为5
semaphore.release(5);
}
}执行结果如下:
txt
主线程执行完毕
Thread-4执行完毕!
Thread-1执行完毕!
Thread-2执行完毕!
Thread-3执行完毕!
Thread-0执行完毕!
Java知识库