同步工具类
JUC
(Java.util.concurrent)是 Java 提供的用于并发编程的工具类库,其中包含了一些通信工具类,用于在多个线程之间进行协调和通信,特别是在多线程和网络通信方面。这些工具类提供了丰富的功能,帮助开发者高效地实现复杂的并发控制和网络通信需求。
Semephore
Semaphore
(信号量)是一种并发控制机制,可用于管理对共享资源的访问以及线程间的同步。Semaphore
通过控制许可数量(permits),实现了对并发线程数的精细管理,有效避免了资源竞争和过载问题,能显著提升系统吞吐量和响应速度。通常被用于限制对某个资源或资源池的并发访问数量。
核心功能
Semaphore
维护了一个计数器,该计数器表示可用的许可数。
限制并发访问量
线程可以通过 acquire()
方法获取一个许可,如果计数器大于0,则线程获取许可并将计数器减1;如果计数器为0,则线程将被阻塞,直到有一个许可可用。
当线程使用完资源后,可以通过 release()
方法释放许可,将计数器增加,以供其他线程获取。
Semaphore
常用于限制并发访问资源的线程数。可以通过构造函数指定初始许可数,或不指定时默认为1。当初始许可数小于线程数时,只能有部分线程能够同时访问资源,其他线程需要等待。
公平与非公平策略
Semaphore
支持公平(Fair)和非公平(Nonfair)两种策略,还可以用于实现线程间的协调和通信。
-
在公平模式下,线程将按照请求许可证的顺序获得许可证,即先请求的线程将先得到许可证。
-
在非公平模式下,线程则可能不按照请求的顺序获得许可证,这可能会导致某些线程饥饿。
例如,可以使用 Semaphore
确保一组线程中的某些线程先执行,然后再由 Semaphore
释放许可,使得其他线程可以执行。
需要注意的是,Semaphore
只是对资源访问进行计数和控制,并不保证线程执行顺序,也不提供锁的锁定和解锁操作。在使用 Semaphore
时,需要遵循正确的获取和释放许可的顺序,以避免死锁或资源泄漏等问题。
灵活性与适应性
Semaphore
的灵活性在于它可以动态地调整许可数量,以适应系统负载的变化。例如,在资源池管理中,可以根据系统的实际使用情况,动态地增加或减少许可数量,以提高资源利用率和系统性能。
常用方法
Semaphore
提供了几个关键的方法来控制许可证的获取和释放:
-
acquire()
:获取一个许可证。如果没有可用的许可证,则当前线程将被阻塞,直到有许可证可用。 -
acquire(int permits)
:一次性获取指定数量的许可证。如果没有足够数量的许可证可用,则当前线程将被阻塞。 -
release()
:释放一个许可证。这将使 Semaphore 的可用许可证数量加一。 -
release(int permits)
:释放指定数量的许可证。 -
tryAcquire()
和tryAcquire(int permits, long timeout, TimeUnit unit)
:尝试获取许可证,如果在指定时间内无法获取到许可证,则返回 false。这些方法不会使线程阻塞。
使用示例
以下是一个简单的示例代码,演示了如何使用 Semaphore
来限制对一组资源的并发访问:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
// 创建一个Semaphore,初始许可为3,表示资源池中最多有3个资源可用
private static final Semaphore semaphore = new Semaphore(3);
public static void main(String[] args) {
// 创建一个固定大小的线程池来模拟客户端请求
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交10个任务到线程池,每个任务代表一个客户端请求
for (int i = 0; i < 10; i++) {
executor.submit(() -> {
try {
// 线程尝试获取许可
semaphore.acquire();
System.out.println("线程" + Thread.currentThread().getName() + "获取到资源,开始处理...");
// 模拟资源处理时间
Thread.sleep((long) (Math.random() * 1000));
System.out.println("线程" + Thread.currentThread().getName() + "处理完毕,释放资源...");
// 线程释放许可
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 关闭线程池
executor.shutdown();
}
}
在这个示例中,我们创建了一个 Semaphore
实例,初始许可设置为3,这意味着最多只能有3个线程同时访问资源。然后,我们创建了一个固定大小为5的线程池来模拟客户端请求,并提交了10个任务到线程池。每个任务都尝试获取 Semaphore
的许可来模拟资源的访问。从输出结果中可以看到,尽管线程池的大小为5,但 Semaphore
确保了同时访问资源的线程数不超过3个。