目录
java.util.concurrent
包中的Semaphore
类解释
java.util.concurrent
包中的Semaphore
类是一个用于控制对共享资源访问的同步辅助类。Semaphore(信号量)的主要作用是限制对某个特定资源的访问线程数目,或者说,它用于控制并发访问共享资源的线程数量。Semaphore可以看作是一个计数器,用于跟踪可用资源的数量。
Semaphore的工作原理基于许可(permits)的概念。在创建Semaphore对象时,你可以指定一个正整数n,表示许可的初始数量。这个数量代表了在没有任何线程占用资源的情况下,可以同时访问该资源的线程数目。每次一个线程想要访问资源时,它必须先通过调用Semaphore的acquire()
方法从Semaphore获得一个许可。如果Semaphore中有可用的许可,线程可以继续执行。如果没有可用的许可,线程将被阻塞,直到有许可可用或者线程被中断。
当线程完成对资源的访问后,它应该释放许可,以便其他线程可以使用。这通过调用release()
方法实现。release()
方法会增加Semaphore中的许可数量,如果有任何线程因为调用acquire()
方法而被阻塞,并且现在有可用的许可,那么这些线程中的一个将被唤醒并允许继续执行。
Semaphore的一个常见用途是控制对某个特定资源的池(如数据库连接池)的访问。例如,如果你有一个数据库连接池,并且希望在任何给定时间最多只有10个线程可以从中获取连接,你可以创建一个初始许可数量为10的Semaphore。这样,每次一个线程想要获取一个数据库连接时,它必须先获得一个许可,这就限制了同时访问数据库连接的线程数目。
Semaphore还支持公平和非公平两种模式。在公平模式下,线程将按照它们请求许可的顺序被授予许可,这有助于避免线程饥饿问题。在非公平模式下,许可的授予顺序是不确定的,这可能会导致某些线程比其他线程更频繁地获得许可,从而提高了吞吐量,但可能会牺牲公平性。
关于java.util.concurrent
包中Semaphore
类的使用例子:
例子1:控制同时访问某个资源的线程数量
假设我们有一个资源,比如一个文件或者一个数据库连接,我们想要限制同时访问这个资源的线程数量。我们可以使用Semaphore
来实现这一点。
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
public static void main(String[] args) {
// 创建一个Semaphore对象,初始值为3,表示最多允许3个线程同时访问资源
Semaphore semaphore = new Semaphore(3);
// 创建5个线程来模拟对资源的访问
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
try {
// 尝试获取许可
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 获取到许可");
// 模拟线程执行一段时间
Thread.sleep(2000);
// 打印线程执行完毕的信息
System.out.println(Thread.currentThread().getName() + " 执行完毕");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放许可
semaphore.release();
System.out.println(Thread.currentThread().getName() + " 释放许可");
}
}).start();
}
}
}
在这个例子中,我们创建了一个初始值为3的Semaphore
对象,表示最多允许3个线程同时访问资源。然后,我们创建了5个线程来模拟对资源的访问。每个线程在访问资源之前都会调用semaphore.acquire()
来获取许可。如果此时没有可用的许可,线程会被阻塞,直到有许可可用。线程在访问完资源后,会调用semaphore.release()
来释放许可。
例子2:模拟抢占停车位
假设我们有一个停车场,只有3个停车位,但是有6辆汽车想要停车。我们可以使用Semaphore
来模拟这个场景。
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
public static void main(String[] args) {
// 创建一个Semaphore对象,初始值为3,表示停车场有3个车位
Semaphore semaphore = new Semaphore(3);
// 创建6个线程模拟6辆汽车
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
try {
// 尝试抢占车位
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "\t抢到车位");
// 停留3秒钟,模拟停留时间
TimeUnit.SECONDS.sleep(3);
// 打印停留3秒后离开车位的信息
System.out.println(Thread.currentThread().getName() + "\t停留3秒后离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放车位
semaphore.release();
}
}, "汽车" + i).start();
}
}
}
在这个例子中,我们创建了一个初始值为3的Semaphore
对象,表示停车场有3个车位。然后,我们创建了6个线程来模拟6辆汽车。每辆汽车在想要停车时都会调用semaphore.acquire()
来尝试抢占车位。如果此时没有可用的车位,汽车线程会被阻塞,直到有车位可用。汽车在停留一段时间后,会调用semaphore.release()
来释放车位。
这两个例子展示了Semaphore
在控制对共享资源访问方面的应用。通过调整Semaphore
的初始值,我们可以灵活地控制同时访问某个资源的线程数量。
如果一个类被多线程调用,则可以使用如下方法对类进行访问控制
@Value("${calculate.semaphoreLimit:3}")
int countLimit;
private static Semaphore semaphore = null;
// 初始时创建一个Semaphore对象,初始值为3,表示最多允许3个线程同时访问资源
@PostConstruct
public void init() {
semaphore = new Semaphore(countLimit);
}
总结
总的来说,Semaphore是一个强大的同步工具,它提供了一种灵活的方式来控制对共享资源的并发访问。