在并发编程的世界里,线程同步是一个至关重要的话题。Java提供了多种同步机制,其中Semaphore
(信号量)是一个非常强大的工具,它可以用来控制对共享资源的访问。在这篇博客中,我们将深入探讨Semaphore
的概念、用法,并通过实例来加深理解。
Semaphore(信号量)是一种计数器,用于管理一定数量的许可(permit)。它主要用于限制可以访问某些资源的线程数量。Semaphore有两种主要操作:
acquire()
:获取一个许可,如果没有可用的许可,当前线程将被阻塞直到有许可被释放。release()
:释放一个许可,增加可用的许可数量。Semaphore可以是公平的(Fair),也可以是非公平的(Nonfair)。公平Semaphore在分配许可时考虑线程的等待时间,而非公平Semaphore则不考虑。
在Java中,Semaphore
类位于java.util.concurrent
包中。下面是如何创建一个Semaphore对象的例子:
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
public static void main(String[] args) {
// 创建一个Semaphore实例,它允许5个线程同时访问
Semaphore semaphore = new Semaphore(5);
// 创建和启动线程
for (int i = 0; i < 10; i++) {
new Thread(new Worker(semaphore, i)).start();
}
}
}
class Worker implements Runnable {
private final Semaphore semaphore;
private final int workerNumber;
Worker(Semaphore semaphore, int workerNumber) {
this.semaphore = semaphore;
this.workerNumber = workerNumber;
}
@Override
public void run() {
try {
// 获取许可
semaphore.acquire();
System.out.println("Worker " + workerNumber + " is working");
// 模拟工作耗时
Thread.sleep(1000);
// 释放许可
semaphore.release();
System.out.println("Worker " + workerNumber + " has finished");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
在上面的例子中,我们创建了一个允许5个线程同时访问的Semaphore。尽管我们启动了10个线程,但是因为Semaphore的限制,任何时候都不会有超过5个线程同时执行工作。
除了基本的acquire()
和release()
方法,Semaphore还提供了tryAcquire()
方法,它尝试获取许可而不是等待直到获得许可。此方法返回一个布尔值,表示是否成功获取了许可。
// 尝试获取许可,立即返回结果
boolean success = semaphore.tryAcquire();
// 尝试获取许可,最多等待指定时间
boolean successWithTimeout = semaphore.tryAcquire(100, TimeUnit.MILLISECONDS);
让我们通过一个实际的例子来看看如何使用Semaphore。假设我们有一个打印机池,里面有3台打印机,但是有多个用户需要打印文档。我们可以使用Semaphore来确保一次只有3个用户可以使用打印机。
import java.util.concurrent.Semaphore;
public class PrinterPool {
// 创建一个Semaphore,允许3个线程同时访问
private final Semaphore semaphore = new Semaphore(3);
public void printDocument(Object document) {
try {
// 获取许可
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " is printing a document");
// 模拟打印耗时
Thread.sleep(3000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 释放许可
semaphore.release();
System.out.println(Thread.currentThread().getName() + " has finished printing");
}
}
public static void main(String[] args) {
PrinterPool printerPool = new PrinterPool();
// 创建和启动线程
for (int i = 0; i < 10; i++) {
final int userNumber = i;
new Thread(() -> printerPool.printDocument("Document " + userNumber), "User " + userNumber).start();
}
}