使用 Semaphore 可以控制同时访问资源的线程个数,在 Java 中,Semaphore 是一个计数信号量。
Semaphore 可以用来限制某个资源的访问线程个数,它的构造函数接收一个整型变量 n,表示同一时刻最多允许 n 个线程访问该资源。当一个线程进入该资源进行访问时,计数器会减去 1,其他线程再访问时就会被阻塞,直到该线程释放资源时计数器加 1。Semaphore 可以控制资源的访问数量,是一个非常重要的并发控制工具。
Semaphore 类的基本用法
Semaphore 类是 Java.util.concurrent 包下的类,使用它需要导入以下包:
import java.util.concurrent.Semaphore;
Semaphore 类的构造器、方法参数都是 int 型的,说明 Semaphore 可以协调的线程数量必须是整数类型,解决某一个范围的访问问题。
Semaphore 类有以下方法:
- acquire(): 获取一个许可证,如果没有可用的许可证,则会阻塞直到有可用的许可证;
- release(): 释放一个许可证,释放一个阻塞的线程;
- tryAcquire(): 尝试获取一个许可证,并立即返回 true 或 false;
- tryAcquire(long timeout, TimeUnit unit): 在指定的时间内尝试获取一个许可证;
- availablePermits(): 获取当前可用的许可证数量。
简单示例:
下面的代码简单演示了如何使用 Semaphore,假设有 10 个资源,现在有 30 个线程需要访问这 10 个资源,每次只能有 3 个线程同时访问,其他线程需要等待当前 3 个线程访问完后才能访问。
import java.util.concurrent.Semaphore;
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3); // 初始化信号量,最多允许 3 个线程访问
for (int i = 0; i < 30; i++) {
Thread t = new Thread(() -> {
try {
semaphore.acquire(); // 获取一个许可证
System.out.println(Thread.currentThread().getName() + "开始访问资源");
Thread.sleep(2000); // 模拟访问资源需要 2s
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); // 释放一个许可证
System.out.println(Thread.currentThread().getName() + "结束访问资源");
}
}, "Thread-" + i);
t.start();
}
}
}
该示例会启动 30 个线程,每个线程尝试去获取一个许可证,当许可证数量不足时,线程会被阻塞,直到获取到许可证后才能继续执行,然后线程释放许可证并结束访问。
Semaphore 与 Condition 一起使用
Semaphore 与 Condition 可以协同工作,Semaphore 负责控制线程的并发访问数量,Condition 负责控制线程的等待和通知,这样可以实现更复杂的并发控制。下面的示例演示了 Semaphore 和 Condition 一起使用的方法。
假设现在有两个线程,线程 1 执行任务 A,线程 2 执行任务 B,任务 B 需要依赖任务 A 的结果,在任务 A 执行完后通知任务 B 开始执行。
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class SemaphoreWithConditionDemo {
private static final Semaphore semaphore1 = new Semaphore(1);
private static final Semaphore semaphore2 = new Semaphore(0);
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
Thread t1 = new Thread(() -> {
lock.lock();
try {
System.out.println("Thread-1执行任务A");
Thread.sleep(2000);
condition.signal(); // 通知线程 2 执行任务B
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "Thread-1");
Thread t2 = new Thread(() -> {
semaphore1.acquire(); // 获取 semaphore 1 的许可证
lock.lock();
try {
System.out.println("Thread-2等待Thread-1执行完任务A,开始执行任务B");
Thread.sleep(2000);
semaphore2.release(); // 释放 semaphore 2 的许可证
System.out.println("Thread-2执行完任务B");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "Thread-2");
t2.start();
t1.start(); // 等待线程 1 完成任务 A,通知线程 2 执行任务 B
lock.lock();
try {
condition.await(); // 等待线程 1 完成任务 A
System.out.println("Thread-1执行完任务A,通知Thread-2执行任务B");
semaphore1.release(); // 释放 semaphore 1 的许可证
semaphore2.acquire(); // 获取 semaphore 2 的许可证
System.out.println("Thread-1继续执行任务");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
该示例会启动两个线程,线程 1 执行任务 A,线程 2 等待线程 1 执行完任务 A 后执行任务 B,任务 B 需要依赖任务 A 的结果,当任务 A 完成后通知线程 2 开始执行任务 B。实现该示例需要使用 Semaphore 和 Condition 类,Semaphore 的作用是限制同时执行任务的线程数量,Condition 用于通知线程 2 开始执行任务 B。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java中的Semaphore如何使用 - Python技术站