Java信号量(Semaphore)

引言

在并发编程中,为了控制多个线程对共享资源的访问,我们需要使用一些同步机制。Java提供了多种同步机制,其中之一就是信号量(Semaphore)。信号量是一种计数器,用来控制能同时访问共享资源的线程数量。本文将详细介绍Java中信号量的概念、用法和示例代码。

什么是信号量?

信号量是一种同步工具,用于保护共享资源。它维护了一个计数器,可以用来控制对共享资源的访问。信号量有两种类型:计数信号量和二进制信号量。

  • 计数信号量:可以控制多个线程同时访问共享资源的数量;
  • 二进制信号量:只能允许一个线程访问共享资源。

Java中的信号量

Java中的信号量是通过java.util.concurrent.Semaphore类实现的。它提供了以下常用方法:

  • acquire():获取一个许可,如果没有可用的许可,则阻塞等待;
  • release():释放一个许可;
  • tryAcquire():尝试获取一个许可,如果成功则返回true,否则返回false
  • tryAcquire(long timeout, TimeUnit unit):在指定的时间内尝试获取一个许可,如果成功则返回true,否则返回false

我们可以使用信号量来控制同时访问某个共享资源的线程数量。当一个线程需要访问共享资源时,它必须先通过acquire()方法获取一个许可。如果许可数量已经达到上限,线程将被阻塞,直到有其他线程释放许可。使用完共享资源后,线程需要通过release()方法释放占用的许可。

示例代码

以下是一个使用信号量的示例代码,模拟了10个线程同时访问一个共享资源的场景。共享资源的访问被限制为同时只能有3个线程进行。

import java.util.concurrent.Semaphore;

public class SharedResource {
    private static Semaphore semaphore = new Semaphore(3);

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new Worker(i));
            thread.start();
        }
    }

    static class Worker implements Runnable {
        private int id;

        public Worker(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            try {
                semaphore.acquire(); // 获取许可
                System.out.println("Thread " + id + " is accessing the shared resource.");
                Thread.sleep(2000); // 模拟访问共享资源的耗时操作
                System.out.println("Thread " + id + " has finished accessing the shared resource.");
                semaphore.release(); // 释放许可
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在上述代码中,我们创建了一个Semaphore对象,并指定了许可数量为3。然后,我们创建了10个线程,每个线程都会尝试获取一个许可,并对共享资源进行访问。当有3个线程获得许可时,其他线程将被阻塞,直到有线程释放许可。

以下是代码运行的示例输出:

Thread 1 is accessing the shared resource.
Thread 2 is accessing the shared resource.
Thread 0 is accessing the shared resource.
Thread 2 has finished accessing the shared resource.
Thread 1 has finished accessing the shared resource.
Thread 0 has finished accessing the shared resource.
Thread 3 is accessing the shared resource.
Thread 4 is accessing the shared resource.
Thread 3 has finished accessing the shared resource.
Thread 5 is accessing the shared resource.
Thread 4 has finished accessing the shared resource.
Thread 6 is accessing the shared resource.
Thread 5 has finished accessing the shared resource.
Thread 6 has finished accessing the shared resource.
Thread 7 is accessing the shared resource.
Thread 8 is accessing the shared resource.
Thread 9 is accessing the shared resource.
Thread 7 has finished accessing the shared resource.
Thread 8 has finished accessing the shared resource.
Thread 9 has finished accessing the shared resource.

如上所示,同时只