当前位置: 面试刷题>> 什么是 Java 的 Semaphore?


在Java并发编程的广阔领域中,Semaphore是一个至关重要的同步工具,它属于java.util.concurrent包。Semaphore(信号量)主要用于控制对共享资源的并发访问数量,确保同一时间访问特定资源的线程数量不会超过设定的限制。这种机制在多种场景下都非常有用,比如数据库连接池、线程池、文件访问控制等,它能够有效地防止资源过载,提高系统的稳定性和性能。

Semaphore的基本概念

Semaphore内部维护了一组虚拟的许可(permits),每个许可代表了一个资源单元或一个访问权限。线程可以通过调用acquire()方法从Semaphore中获取许可,如果此时没有可用的许可,线程将会被阻塞,直到有许可可用。同样地,线程可以通过调用release()方法释放一个许可,从而允许其他等待的线程继续执行。

Semaphore的构造方法

Semaphore提供了几个构造方法,但最常用的两个是:

  • Semaphore(int permits):创建一个具有给定许可数和非公平锁定策略的Semaphore
  • Semaphore(int permits, boolean fair):创建一个具有给定许可数和给定公平性的Semaphore。如果fairtrue,则线程将按照它们请求许可的顺序(FIFO)来获取许可,但这可能会降低并发性。

Semaphore的使用示例

假设我们有一个资源池,比如一个数据库连接池,它最多只能同时处理5个连接请求。我们可以使用Semaphore来管理这个限制:

import java.util.concurrent.Semaphore;

public class DatabaseConnectionPool {
    private final Semaphore semaphore = new Semaphore(5); // 最多5个并发连接

    public void acquireConnection() throws InterruptedException {
        semaphore.acquire(); // 请求一个许可
        System.out.println("Acquired a connection from the pool.");
        // 模拟数据库操作
        Thread.sleep(1000);
        releaseConnection();
    }

    public void releaseConnection() {
        semaphore.release(); // 释放一个许可
        System.out.println("Released a connection back to the pool.");
    }

    public static void main(String[] args) {
        DatabaseConnectionPool pool = new DatabaseConnectionPool();

        // 模拟多个线程请求数据库连接
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    pool.acquireConnection();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }).start();
        }
    }
}

在这个例子中,DatabaseConnectionPool类使用了一个Semaphore来限制同时访问数据库连接的线程数。当线程尝试通过调用acquireConnection()方法获取连接时,如果所有连接都在使用中,线程将会被阻塞,直到有连接被释放(即调用releaseConnection()方法)。

高级特性与注意事项

  • 尝试非阻塞获取许可Semaphore还提供了tryAcquire()方法,它尝试获取许可但不阻塞当前线程,如果获取不到许可,则立即返回false
  • 超时获取许可tryAcquire(long timeout, TimeUnit unit)方法允许线程在指定的时间内尝试获取许可,如果超时仍未获取到许可,则返回false
  • 公平性:虽然大多数情况下使用非公平锁(默认)可以获得更好的性能,但在某些场景下,如需要严格按照请求顺序处理任务的场景,使用公平锁可能更为合适。
  • 资源泄露:在使用Semaphore时,必须确保每个acquire()调用最终都有一个对应的release()调用,否则可能会导致资源泄露,即许可永远不会被释放,新的线程将永远无法获取许可。

总结

Semaphore是Java并发编程中一个非常强大的工具,它允许开发者精细地控制对共享资源的并发访问。通过合理使用Semaphore,我们可以有效地避免资源过载,提高系统的稳定性和性能。在设计和实现并发系统时,深入理解并掌握Semaphore的使用是非常重要的。在码小课网站上,你可以找到更多关于Java并发编程的深入讲解和实战案例,帮助你进一步提升自己的编程技能。

推荐面试题