HOOOS

深入解析 Java 并发中的 Semaphore:原理与源码剖析

0 57 码农小高 Java并发编程Semaphore
Apple

在 Java 并发编程中,Semaphore 是一个非常重要的同步工具,用于控制对共享资源的访问。它的核心思想是通过一个计数器来限制同时访问某一资源的线程数量。本文将从底层原理、内部数据结构以及线程调度机制入手,结合源码进行深入分析,帮助你更好地理解 Semaphore 的工作机制。

Semaphore 的基本概念

Semaphore 直译为信号量,用于管理一组许可证(permits)。通过 acquire() 方法获取许可证,release() 方法释放许可证。它的核心功能是控制并发线程的数量,确保不会因为过多的线程同时访问某一资源而导致系统崩溃或性能下降。

Semaphore 的实现原理

Semaphore 的实现依赖于 AbstractQueuedSynchronizer(AQS),这是 Java 并发包中的一个底层框架。AQS 提供了一种基于 FIFO 队列的同步机制,Semaphore 利用 AQS 的共享模式来实现许可证的管理。

以下是 Semaphore 的核心源码片段:

public class Semaphore implements java.io.Serializable {
    private final Sync sync;
    abstract static class Sync extends AbstractQueuedSynchronizer {
        Sync(int permits) {
            setState(permits);
        }
        final int getPermits() {
            return getState();
        }
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 || compareAndSetState(available, remaining))
                    return remaining;
            }
        }
        protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))
                    return true;
            }
        }
    }
}

从源码中可以看出,Semaphore 的核心逻辑是通过 CAS(Compare And Swap)操作来管理许可证的数量。nonfairTryAcquireShared 方法用于尝试获取许可证,而 tryReleaseShared 方法用于释放许可证。

内部数据结构

Semaphore 的内部数据结构主要依赖于 AQS 的状态变量 state,用于记录当前剩余的许可证数量。AQS 的 FIFO 队列用于管理等待获取许可证的线程。

线程调度机制

当一个线程尝试获取许可证时,如果许可证数量不足,线程会被放入 AQS 的等待队列中,并进入阻塞状态。当其他线程释放许可证时,AQS 会唤醒等待队列中的线程,使其重新尝试获取许可证。

公平性与非公平性

Semaphore 支持公平模式和非公平模式。在公平模式下,线程获取许可证的顺序与其请求的顺序一致,即遵循 FIFO 原则;在非公平模式下,线程可以插队获取许可证,从而提高吞吐量,但可能导致某些线程长时间无法获取资源。

实际应用场景

Semaphore 常用于资源池的管理,例如数据库连接池、线程池等。通过限制并发访问的资源数量,可以有效避免资源耗尽的问题。

示例代码

以下是一个简单的 Semaphore 使用示例:

public class SemaphoreExample {
    private static final int THREAD_COUNT = 10;
    private static final Semaphore semaphore = new Semaphore(5); // 最多允许 5 个线程同时执行

    public static void main(String[] args) {
        for (int i = 0; i < THREAD_COUNT; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + " 获取许可证");
                    Thread.sleep(2000); // 模拟任务执行
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                    System.out.println(Thread.currentThread().getName() + " 释放许可证");
                }
            }).start();
        }
    }
}

总结

Semaphore 是一个强大的同步工具,能够有效控制并发线程的数量。通过对其底层原理和源码的深入分析,我们可以更好地理解其工作机制,并在实际开发中灵活运用。希望本文能帮助你加深对 Semaphore 的理解,并为你的并发编程实践提供有价值的参考。

点评评价

captcha
健康