Java 入门指南:Java 并发编程 —— 同步工具类 Semephore(信号量)

文章目录

    • 同步工具类
    • Semephore
      • 核心功能
        • 限制并发访问量
        • 公平与非公平策略
        • 灵活性与适应性
      • 常用方法
      • 使用示例

同步工具类

JUC(Java.util.concurrent)是 Java 提供的用于并发编程的工具类库,其中包含了一些通信工具类,用于在多个线程之间进行协调和通信,特别是在多线程和网络通信方面。这些工具类提供了丰富的功能,帮助开发者高效地实现复杂的并发控制和网络通信需求。

![[JUC Communication Utilities.png]]

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个。

你可能感兴趣的:(Java,java,开发语言,intellij-idea,个人开发,团队开发,java-ee)