返回

多路并发下的Semaphore:JUC之AQS

见解分享

在Java并发编程中,synchronized 是一种常用的同步机制,它提供互斥锁语义,确保同一时刻只有一个线程可以获取执行代码的锁。然而,在某些场景下,我们需要使用多把锁来协调多个线程之间的访问,此时 Semaphore 就派上用场了。

Semaphore

Semaphore(信号量)是一种用于限制对共享资源访问数量的同步器。它允许指定数量的线程同时访问该资源,超过该数量的线程将被阻塞。

在Java中,Semaphore 被实现为 java.util.concurrent.Semaphore 类,它基于 AQS (AbstractQueuedSynchronizer)框架构建。AQS 是一个同步队列,它使用一个队列来管理等待获取锁的线程。

Semaphore 的工作原理

Semaphore 使用一个整数值 permits 来表示可用的许可证数量。当一个线程想要获取一个许可证时,它会调用 acquire() 方法。如果 permits 大于 0,该线程将获得许可证并继续执行。否则,该线程将被阻塞,直到有许可证可用。

当一个线程完成对资源的访问后,它会调用 release() 方法来释放许可证。这会增加 permits 的值,从而允许另一个线程获取许可证。

Semaphore 的特性

  • 公平性: AQS 框架保证了线程获取许可证的公平性,即先请求的线程将优先获取许可证。
  • 可重入性: 同一个线程可以多次获取同一个许可证,而不会被阻塞。
  • 超时: 我们可以设置一个超时时间,如果在指定时间内没有获取到许可证,则抛出异常。

Semaphore 的应用场景

Semaphore 有着广泛的应用场景,例如:

  • 资源池管理: 限制对资源池中资源的并发访问数量。
  • 限流: 控制对服务的并发请求数量。
  • 多线程协调: 协调多个线程之间的访问顺序。

Semaphore 的示例

import java.util.concurrent.Semaphore;

public class SemaphoreExample {

    private static final Semaphore semaphore = new Semaphore(3);

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    // 获取许可证
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + " 获得许可证");
                    // 模拟资源访问
                    Thread.sleep(1000);
                    // 释放许可证
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

在示例中:

  • 我们创建了一个具有 3 个许可证的 Semaphore。
  • 10 个线程同时并发执行,试图获取许可证。
  • 只有 3 个线程可以同时获取许可证,其余线程将被阻塞。
  • 当线程完成资源访问后,它们释放许可证,允许其他线程继续执行。