1. Java 中 Semaphore 的定义与原理

Semaphore 是 Java 并发包 java.util.concurrent 里的同步工具类,可用来控制同时访问特定资源的线程数量。其内部借助一个计数器来实现,创建 Semaphore 对象时需要指定计数器的初始值,此值代表可同时访问资源的线程数量上限。

线程在访问资源前需先调用 acquire() 方法获取许可,若此时计数器的值大于 0,计数器会减 1,线程获取许可并可以访问资源;若计数器的值为 0,线程会被阻塞,直到有其他线程释放许可。线程访问完资源后,需调用 release() 方法释放许可,这时计数器的值会加 1。

2. 作用

  • 资源访问控制:对同时访问特定资源的线程数量进行限制,防止资源被过度使用。例如,在数据库连接池中,可使用 Semaphore 限制同时使用的数据库连接数量,避免数据库连接耗尽。
  • 流量控制:在高并发场景下,可对进入系统的请求数量进行控制,防止系统因过多请求而崩溃。

3. 应用场景

3.1 数据库连接池

数据库连接是有限的资源,为避免过多线程同时请求数据库连接导致连接耗尽,可以使用 Semaphore 来控制并发访问数据库连接的线程数量。以下是示例代码:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.concurrent.Semaphore;

class DatabaseConnectionPool {
    private static final int MAX_CONNECTIONS = 5;
    private final Semaphore semaphore = new Semaphore(MAX_CONNECTIONS);

    public Connection getConnection() throws InterruptedException, SQLException {
        // 获取许可
        semaphore.acquire();
        try {
            // 模拟获取数据库连接
            return DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "username", "password");
        } catch (SQLException e) {
            // 若获取连接失败,释放许可
            semaphore.release();
            throw e;
        }
    }

    public void releaseConnection(Connection connection) {
        try {
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 释放许可
            semaphore.release();
        }
    }
}

3.2 限流场景

在高并发的 Web 应用中,为防止过多请求同时涌入导致系统崩溃,可以使用 Semaphore 对请求进行限流。以下是简单示例:

import java.util.concurrent.Semaphore;

class RequestLimiter {
    private static final int MAX_REQUESTS = 100;
    private final Semaphore semaphore = new Semaphore(MAX_REQUESTS);

    public boolean tryProcessRequest() {
        // 尝试获取许可
        return semaphore.tryAcquire();
    }

    public void releaseRequest() {
        // 释放许可
        semaphore.release();
    }
}

3.3 多线程文件下载

在多线程文件下载场景中,为避免同时下载的文件数量过多导致网络带宽被占满,可以使用 Semaphore 控制同时下载的线程数量。以下是示例代码:

import java.util.concurrent.Semaphore;

class FileDownloader {
    private static final int MAX_DOWNLOADS = 3;
    private final Semaphore semaphore = new Semaphore(MAX_DOWNLOADS);

    public void downloadFile(String url) {
        try {
            // 获取许可
            semaphore.acquire();
            System.out.println("开始下载文件: " + url);
            // 模拟文件下载
            Thread.sleep(2000);
            System.out.println("文件下载完成: " + url);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 释放许可
            semaphore.release();
        }
    }
}

通过这些例子可以看出,Semaphore 在需要控制并发访问资源数量的场景中非常有用。