Semaphore 是 Java.util.concurrent 包中提供的一个线程同步工具类,它可以用来控制同时访问某个资源的线程数量。


Semaphore 代码示例 1 :

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    public static void main(String[] args) {
        // 创建一个 Semaphore 对象,初始值为 3
        Semaphore semaphore = new Semaphore(3);

        // 创建 5 个线程
        for (int i = 1; i <= 5; i++) {
            new Thread(() -> {
                try {
                    // 尝试获取许可
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + " 获取到许可");
                    // 模拟线程执行一段时间
                    Thread.sleep(2000);
                    System.out.println(Thread.currentThread().getName() + " 执行完毕");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 释放许可
                    semaphore.release();
                    System.out.println(Thread.currentThread().getName() + " 释放许可");
                }
            }).start();
        }
    }
}

代码解析:

  1. 创建一个 Semaphore 对象 semaphore,初始值为 3,表示最多允许 3 个线程同时访问资源。
  2. 创建 5 个线程,每个线程执行的任务是:

    • 尝试获取许可,如果没有可用的许可,线程会被阻塞,直到有许可可用。
    • 打印获取到许可的信息。
    • 模拟线程执行一段时间(这里使用 Thread.sleep(2000) 模拟)。
    • 打印线程执行完毕的信息。
    • 释放许可。
  3. 启动线程,让它们开始执行任务。

可能的输出结果:

Thread-1 获取到许可
Thread-2 获取到许可
Thread-3 获取到许可
Thread-1 执行完毕
Thread-1 释放许可
Thread-2 执行完毕
Thread-2 释放许可
Thread-3 执行完毕
Thread-3 释放许可
Thread-4 获取到许可
Thread-4 执行完毕
Thread-4 释放许可
Thread-5 获取到许可
Thread-5 执行完毕
Thread-5 释放许可

在这个示例中,最多允许 3 个线程同时获取许可,其他线程需要等待有许可可用时才能获取。每个线程获取到许可后,执行一段时间后释放许可。请注意,输出的顺序可能会有所不同,因为线程的执行顺序是不确定的。



Semaphore 代码示例 2 :

下面是一个使用 Semaphore 模拟抢占停车位的代码示例:

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreDemo {
    public static void main(String[] args) {
        // 创建一个 Semaphore 对象,初始值为 3,表示停车场有 3 个车位
        Semaphore semaphore = new Semaphore(3);

        // 创建 6 个线程模拟 6 辆汽车
        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                try {
                    // 尝试抢占车位
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "\t抢到车位");
                    try {
                        // 停留 3 秒
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "\t停留 3 秒后离开车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 释放车位
                    semaphore.release();
                }
            }, "汽车" + i).start();
        }
    }
}

代码解析:

  1. 创建一个 Semaphore 对象 semaphore,并将初始值设为 3,表示停车场有 3 个车位。
  2. 使用 for 循环创建 6 个线程,模拟 6 辆汽车。
  3. 每个线程执行的任务是:

    • 尝试通过 semaphore.acquire() 抢占车位。如果车位已满,线程会被阻塞,直到有车位可用。
    • 打印抢到车位的信息。
    • 停留 3 秒钟,使用 TimeUnit.SECONDS.sleep(3) 模拟停留时间。
    • 打印停留 3 秒后离开车位的信息。
    • 通过 semaphore.release() 释放车位。
  4. 启动线程,让它们开始执行任务。

可能的打印结果:

汽车3    抢到车位
汽车2    抢到车位
汽车1    抢到车位
汽车3    停留 3 秒后离开车位
汽车1    停留 3 秒后离开车位
汽车2    停留 3 秒后离开车位
汽车6    抢到车位
汽车4    抢到车位
汽车5    抢到车位
汽车6    停留 3 秒后离开车位
汽车4    停留 3 秒后离开车位
汽车5    停留 3 秒后离开车位

请注意,由于线程的执行顺序是不确定的,实际的输出顺序可能会有所不同。但是,无论输出顺序如何,最终每辆汽车都会抢到车位、停留 3 秒后离开。最多只能有 3 辆汽车同时停在停车位上,其他汽车需要等待前面的汽车离开才能抢占车位。



Semaphore 底层原理涉及到线程同步和共享变量的操作


Semaphore是Java.util.concurrent包中提供的一个线程同步工具类,它可以用来控制同时访问某个资源的线程数量。Semaphore内部使用了共享模式,并基于AQS(AbstractQueuedSynchronizer)来实现线程的同步和互斥。

Semaphore的底层原理是基于AQS的共享模式实现的。AQS维护了一个共享的同步状态(state)和一个等待队列。Semaphore中的同步状态表示可用的许可证的数量,而等待队列中的线程表示等待获取许可证的线程。

具体来说,Semaphore的主要方法是acquire()和release()。当一个线程调用acquire()方法时,会尝试获取一个许可证,即将同步状态的值减1。如果此时同步状态的值大于等于0,则表示成功获取到许可证,线程可以继续执行后续的代码。如果同步状态的值小于0,则表示许可证已经全部被占用,线程会进入阻塞状态,被加入到等待队列中。

当有一个线程调用release()方法时,会释放一个许可证,即将同步状态的值加1。如果此时等待队列中有等待获取许可证的线程,它们会被唤醒并尝试再次获取许可证。如果没有等待的线程,许可证会被存储在Semaphore中,供后续的acquire()方法调用使用。

Semaphore的底层原理与CountDownLatch和CyclicBarrier有所不同。CountDownLatch和CyclicBarrier都是基于AQS的共享模式实现的,但它们的同步状态是通过计数器实现的。而Semaphore的同步状态是表示可用的许可证的数量。通过控制许可证的数量,就可以控制同时访问某个资源的线程数量。

总结起来,Semaphore使用AQS的共享模式来实现线程的同步和互斥。通过控制许可证的数量,Semaphore可以限制同时访问某个资源的线程数量。它可以应用于线程池、资源池等场景,实现对资源的有效利用和线程的合理调度。


今夜有点儿凉
40 声望1 粉丝

今夜有点儿凉,乌云遮住了月亮。