Semaphore

    Semaphore是JDK1.5位于java.util.concurrent包下的一个计数信号量。Semaphore通常用于限制可以访问某些资源的线程数目。它内部维护了一个许可集。在许可可用之前会阻塞每一个acquire(),然后再获取该许可;每个release()可以添加一个许可,从而可能释放一个正在阻塞的获得者。

 

    Semaphore有两个构造器,Semaphore(int permits)Semaphore(int permits, boolean fair),其中参数permits表示初始的可用许可数目;booleanfair可以让我们选择是否接受一个公平的参数,为false时此类不对线程获取许可的顺序做任何保证,也就是说线程获取许可是随机的。为true时信号量保证对于任何调用获取方法的线程而言,都按照处理它们调用这些方法的顺序(即先进先出;FIFO)来选择线程、获得许可。如果不设置的话默认为false

 

    在Semaphore的所有方法中,acquirerelease是最为重要的,两者都有重载方法。下面分别介绍其中的不同。

    acquire():返回值为void。表示从此信号量获取一个许可,并将可用许可数减1。如果当前信号量没有许可可获取,线程将被阻塞,否则线程将被中断。

    acquire(int permits):与acquire()不同的是此方法一次可以获取指定数目permits的许可,并将可用许可数减去permits

    tryAcquire():返回值为boolean。仅在调用此信号量存在一个可用许可时才从此信号量获取许可,并将可用许可数减1,同时返回true。如果没有可用的许可,此方法不会使线程阻塞,但是将立刻返回false。需要注意的是,此方法不使用公平设置,而是使用任意可用的许可,也就是说即使在构造器中设置了使用公平排序策略,但是调用此方法时也将立刻获取许可(如果有一个许可可用的话),而不管当前是否有正在等待的线程。

    tryAcquire(int permits)tryAcquire()一样,不过此方法允许获取多个许可。

    tryAcquire(long timeout, TimeUnit unit):tryAcquire()一样,不过此方法允许设置等待时间。如果超出等待时间则返回false

    tryAcquire(int permits, long timeout, TimeUnit unit):不再解释。

    release():与acquire()对应,返回值为void。用于释放一个许可,并将其返回给信号量,同时将可用许可数加1。不要求释放许可的线程必须通过调用acquire来获取许可。如果任意线程试图获取许可,则选中一个线程并将刚刚释放的许可给予它。

    release(int permits):与acquire(int permits)相对应,不解释。

 

    下面还是跟CyclicBarrier一样,举个吃饭的例子来体会Semaphore的用法。

    问题:假设“祥来饭店”只有5张桌子,这时候陆续来了8个人去该饭店吃饭(一个人一张桌),那些后来的顾客实在是不想走,因为该饭店做的东西真的是太好吃了,怎么办呢?只能等了。等待有桌子空闲了,他们才能开始享受美食的诱惑。而且要求先等待的先获取桌子的使用权。问,怎样用代码体现出来。

 

    对于上面的问题,可以创建一个Semaphore,参数permits5,表示5张桌子。同时开启8个线程表示8位去此饭店吃饭的顾客。前5位顾客去的时候刚好有位置,也就是说都获得了桌子(许可),后面顾客来的时候桌子刚好满了(没许可了),此时他们只能排队等候(当试图获取许可的时候被阻塞了),直达有桌子空闲(许可被释放)。

 

    基于以上分析,代码如下:


package com.gk.thread.semaphore;

import java.util.concurrent.Semaphore;

public class Eat {
	
	public static void main(String[] args) throws InterruptedException {
		
		Semaphore sp = new Semaphore(5, true);			// 使用公平的排序
		
		Runnable r = new EatRunnable(sp);

		for (int i=1; i<=8; i++) {
			String name = "customer" + i;
			new Thread(r, name).start();
		}
		
	}

}


package com.gk.thread.semaphore;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Semaphore;

public class EatRunnable implements Runnable {

	private Semaphore sp;
	
	public EatRunnable(Semaphore sp) {
		this.sp = sp;
	}
	
	@Override
	public void run() {
		
		try {
			
			Thread.sleep((long)(Math.random() * 1000) * 10);		// 模拟先后到达饭店
			
			/*
			 * availablePermits()方法可以获取当前信号量中可用的许可数
			 */
			if (sp.availablePermits() <= 0) {	// 判断当前是否还有许可
				
				System.out.println("当前饭店已经没座位了," + Thread.currentThread().getName() + "请排队等候...");
			}
			
			sp.acquire();	// 获取一个许可
			
			System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "  -->  " + 
								Thread.currentThread().getName() + "来了======^_^======");
			
			Thread.sleep((long)(Math.random() * 1000) * 60);	// 模拟吃饭时间
			
			System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "  -->  " + 
					Thread.currentThread().getName() + "走了...");
			
			sp.release();	// 释放许可
			
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}
		
	}

}



    从运行结果可以看出,有8个线程同时执行,但是只有5个许可,所以多出来的3个只能排队等待了。当有线程释放了许可之后,按照线程排队等待的顺序,先等待的先获得许可往下执行。


    此外,基于Semaphore的特性,如果将信号量初始化为1,使得它在使用时最多只有一个可用的许可,从而可用作一个相互排斥的锁。这通常也称为二进制信号量。另请参见《同步之Semaphore信号量》




<think>好的,我需要帮助用户了解如何使用Semaphore进行并发控制,并给出示例代码。首先,我要回忆Semaphore的基本概念和工作原理。根据引用中的信息,Semaphore通过信号量控制并发数量,acquire减少许可,release增加许可,超过容量的线程需要等待。 接下来,用户需要具体的使用方法和示例代码。引用里提到了Python的asyncio.Semaphore和Java的示例。用户可能对Python或Java感兴趣,但问题中没有明确指定,所以我应该覆盖两种常见语言,比如Python和Java,或者选一个主要的。但根据用户提供的引用,引用1是Python的,引用3、4、5是Java的。可能用户希望看到两种语言的示例?或者需要明确询问?但根据用户最后的提问,直接要求示例代码,可能需要多语言示例。 不过,根据系统指令,用户不可见层需要生成相关问题,而用户可见层需要结构清晰的回答。我应该先解释Semaphore的基本使用步骤,然后分语言给出示例,并引用相关文献。 需要确保代码语法正确,Python使用async with,Java使用acquire和release。同时注意引用标记,比如引用1和引用4。然后生成相关问题。 现在组织内容:首先介绍Semaphore的作用,然后分步骤说明使用方法,接着给出Python和Java的代码示例,每个示例后附上引用来源。最后生成相关问题。 检查是否符合所有系统指令:行内公式用$...$,但这个问题可能不涉及数学公式。独立公式不需要。中文回答,结构清晰,正确引用,生成相关问题。确认无误后输出。</think>### Semaphore 的并发控制使用方法 #### 1. 基本使用步骤 - **创建 Semaphore 对象**:指定最大并发数量(许可数)[^2][^4] - **获取许可**:在需要控制并发的代码块前调用 `acquire()` 或通过上下文管理器进入 - **释放许可**:在代码块执行完成后调用 `release()` 或通过上下文管理器自动释放 #### 2. Python 示例(异步场景) ```python import asyncio async def task(semaphore, id): async with semaphore: # 自动获取/释放许可 print(f"任务{id}开始执行") await asyncio.sleep(1) print(f"任务{id}执行完成") async def main(): semaphore = asyncio.Semaphore(3) # 最大并发3 tasks = [task(semaphore, i) for i in range(5)] await asyncio.gather(*tasks) asyncio.run(main()) ``` *引用自异步编程实现[^1]* #### 3. Java 示例(线程同步) ```java import java.util.concurrent.Semaphore; public class Demo { private static final Semaphore semaphore = new Semaphore(3); // 最大并发3 public static void main(String[] args) { for (int i = 0; i < 5; i++) { new Thread(() -> { try { semaphore.acquire(); // 获取许可 System.out.println("任务开始:" + Thread.currentThread().getId()); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); // 释放许可 System.out.println("任务完成:" + Thread.currentThread().getId()); } }).start(); } } } ``` *引用自线程并发控制实现[^4]* #### 4. 关键机制 - 当许可耗尽时,后续线程进入阻塞状态 - 公平性可通过构造函数参数配置(Java 支持公平锁)[^4] - 支持尝试获取许可(`tryAcquire`)和超时机制[^5] §§ 相关问题 §§ 1. Semaphore 与 Mutex 锁有什么区别? 2. 如何防止 Semaphore 的许可泄漏问题? 3. Semaphore 在数据库连接池中的应用? 4. 如何实现 Semaphore 的跨进程控制? 5. Semaphore 的公平模式对性能有什么影响? : Python异步信号量实现 [^2]: Semaphore基础工作机制 : Java并发控制示例 : 单许可信号量实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值