AQS组件之Semaphore

本文详细解析了Java中的Semaphore(信号量)概念,一种用于控制同时访问特定资源的线程数量的同步工具。通过实例展示了如何使用Semaphore来限制并发访问,包括公平与非公平模式的区别,以及如何在实际应用中正确配置和使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在前两篇说了AQS和CountDownLatch,现在我们来看看Semaphore。

synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个资源,Semaphore(信号量)可以指定多个线程同时访问某个资源。

​ 用于保证同一时间并发访问线程的数目。信号量在操作系统中是很重要的概念,Java并发库里的Semaphore就可以很轻松的完成类似操作系统信号量的控制。Semaphore可以很容易控制系统中某个资源被同时访问的线程个数。在前面的链表中,链表正常是可以保存无限个节点的,而Semaphore可以实现有限大小的列表。

例子:

public class SemaphoreExample1 {
	// 请求的数量
	private static final int threadCount = 550;

	public static void main(String[] args) throws InterruptedException {
		// 创建一个具有固定线程数量的线程池对象(如果这里线程池的线程数量给太少的话你会发现执行的很慢)
		ExecutorService threadPool = Executors.newFixedThreadPool(300);
		// 一次只能允许执行的线程数量。
		final Semaphore semaphore = new Semaphore(20);

		for (int i = 0; i < threadCount; i++) {
			final int threadnum = i;
			threadPool.execute(() -> {// Lambda 表达式的运用
				try {
					semaphore.acquire();// 获取一个许可,所以可运行线程数量为20/1=20
					test(threadnum);
					semaphore.release();// 释放一个许可
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
//                try {
//                    //尝试获取许可一段时间,获取不到不执行
//                    // 参数1:等待时间长度  参数2:等待时间单位
//                    if (semaphore.tryAcquire(5000,TimeUnit.MILLISECONDS)){
//                        test(threadNum);//需要并发控制的内容
//                        semaphore.release(); // 释放许可
//                    }
//                } catch (Exception e) {
//                    log.error("exception", e);
//                }

			});
		}
		threadPool.shutdown();
		System.out.println("finish");
	}

	public static void test(int threadnum) throws InterruptedException {
		Thread.sleep(1000);// 模拟请求的耗时操作
		System.out.println("threadnum:" + threadnum);
		Thread.sleep(1000);// 模拟请求的耗时操作
	}
}

执行 acquire 方法阻塞,直到有一个许可证可以获得然后拿走一个许可证;每个 release 方法增加一个许可证,这可能会释放一个阻塞的acquire方法。然而,其实并没有实际的许可证这个对象,Semaphore只是维持了一个可获得许可证的数量。 Semaphore经常用于限制获取某种资源的线程数量。

当然一次也可以一次拿取和释放多个许可,不过一般没有必要这样做:

	semaphore.acquire(5);// 获取5个许可,所以可运行线程数量为20/5=4
	test(threadnum);
	semaphore.release(5);// 获取5个许可,所以可运行线程数量为20/5=4

除了 acquire方法之外,另一个比较常用的与之对应的方法是tryAcquire方法,该方法如果获取不到许可就立即返回false。

Semaphore 有两种模式,公平模式和非公平模式。

  • 公平模式: 调用acquire的顺序就是获取许可证的顺序,遵循FIFO;

  • 非公平模式: 抢占式的。

Semaphore 对应的两个构造方法如下:

   public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }

    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

这两个构造方法,都必须提供许可的数量,第二个构造方法可以指定是公平模式还是非公平模式,默认非公平模式。

具体可看:https://blog.csdn.net/qq_19431333/article/details/70212663

 

参照:https://blog.csdn.net/jesonjoke/article/details/80054133

https://blog.csdn.net/xlgen157387/article/details/78218736

AQS(AbstractQueuedSynchronizer)是 Java 并发包中一个重要的同步组件,它通过内部维护一个 FIFO 队列来实现同步协作,是很多同步组件的基础,例如 ReentrantLock、CountDownLatch、Semaphore 等。 AQS 内部基于一个 int 类型的变量 state 来表示同步状态,当 state 为 0 时表示未锁定状态,当 state 大于 0 时表示已锁定状态,小于 0 时表示 state 的绝对值是等待获取同步状态的线程个数。AQS 的操作主要包括独占和共享两种模式,独占模式下只有一个线程能够获取同步状态,共享模式下可以有多个线程同时获取同步状态。 AQS 的核心是一个双向链表,其中每个节点表示一个等待线程,通过 CAS 操作来实现节点的加入和移除。在独占模式下,等待线程会被加入到队列的尾部,当前获取同步状态的线程是队列头部的节点,当当前线程释放同步状态后,会唤醒队列中的下一个节点。在共享模式下,等待线程也会被加入到队列的尾部,但是当前获取同步状态的线程可能是队列中的任意一个节点,当当前线程释放同步状态后,会唤醒队列中的所有节点。 AQS 组件包括以下几种: 1. ReentrantLock:可重入锁,支持公平和非公平两种模式。 2. ReentrantReadWriteLock:读写锁,支持读写分离。 3. Semaphore:信号量,用于控制资源的并发访问量。 4. CountDownLatch:倒计时门闩,用于等待其他线程完成一定的操作后再执行。 5. CyclicBarrier:循环屏障,用于等待一组线程达到某个状态后再执行。 6. Condition:条件变量,用于在某个条件下等待或唤醒线程。 以上就是 AQS 的基本原理和组件介绍。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值