Semaphore(计数信号量) ~java并发编程

Semaphore是Java并发工具类,用于控制同时访问特定资源的线程数量。本文详细介绍了Semaphore的用法,包括构造函数、acquire和release方法,并通过实例展示了如何在多线程环境中使用Semaphore控制资源的并发访问。分析了不同场景下Semaphore的行为,如线程获取和释放许可证的过程。
摘要由CSDN通过智能技术生成

1、Semaphore含义

Semaphore称为计数信号量,它允许n个任务同时访问某个资源,可以将信号量看做是在向外分发使用资源的许可证,只有成功获取许可证,才能使用资源。

2、Semaphore用法

示例:定义一组许可证,假设许可证数量为10,t1线程需要消费5个许可证,t1执行,t2线程需要消费6个许可证,当前许可证数量为(10-5=5)t2阻塞,只有t1释放之后,许可证数量大于等于6,t2执行。

3、Semaphore(信号量)主要方法使用说明

1.1、

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

        该构造函数会创建具有给定的许可数  和 默认为非公平的许可证数量

1.2、

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

        该构造函数会创建具有给定的许可数  和 可选择的是否公平   的许可证数量

2、

 public void acquire(int permits) throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
    }

此方法从信号量获取一个(多个)许可,在提供一个许可前一直将线程阻塞,或者线程被中断

3、

public void release(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
    }

此方法释放一个(多个)许可,将其返回给信号量

4、使用Semaphore的例子

说明: 该例子使用3个线程检验许可证的数量是否满足线程中要求的情况下验证结果


/**
 * Semaphore示例
 * 
 * @author zl
 *
 */
public class SemaphoreDemo {
	// 初始设置为10个许可证
	public final static int SEM_USE_SIZE = 10;

	public static void main(String[] args) {
		Semaphore semaphore = new Semaphore(SEM_USE_SIZE);
		// 创建两个线程执行
		MyThread t1 = new MyThread("t1", semaphore);
		MyThread t2 = new MyThread("t2", semaphore);
		t1.start();
		t2.start();
		// 主线程执行需要的5个许可证
		int permits = 5;
		System.out.println(Thread.currentThread().getName() + " trying to acquire");
		try {
			// 主线程需要5个许可证
			semaphore.acquire(permits);
			System.out.println(Thread.currentThread().getName() + " acquire successfully");
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			// 释放许可证
			semaphore.release(permits);
			System.out.println(Thread.currentThread().getName() + " release successfully");
		}
	}
}

class MyThread extends Thread {
	private Semaphore semaphore;

	public MyThread(String name, Semaphore semaphore) {
		super(name);
		this.semaphore = semaphore;
	}

	@Override
	public void run() {
		// 该线程执行需要3个许可证
		int count = 3;
		System.out.println(Thread.currentThread().getName() + " trying to acquire");
		try {
			// 如果许可证大于count,代表能够取到,线程执行,否则阻塞
			semaphore.acquire(count);
			System.out.println(Thread.currentThread().getName() + " acquire successfully");
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			// 当线程执行完或者被打断后,归还许可证
			semaphore.release(count);
			System.out.println(Thread.currentThread().getName() + " release successfully");
		}
	}
}

其中某一次的运行结果

main trying to acquire                //main线程开始获取许可
main acquire successfully         //main线程成功获取许可,此时许可证数量为10-5=5
t1 trying to acquire                    //t1线程开始获取许可
t1 acquire successfully             //t1线程成功获取许可,此时许可证数量为5-3=2
t2 trying to acquire             //t2线程开始获取许可 ,t2需要3个许可,但总许可证只有2,阻塞
main release successfully  //main线程结束,释放许可证,总许可证为2+5=7
t2 acquire successfully        //许可证数量满足t2线程要求,t2线程成功获取许可
t1 release successfully
t2 release successfully

5、说明

1、semaphore初始化有1个令牌,1个线程调用一次acquire方法,然后调用两次release方法,之后另外一个线程调用acquire(2)方法,此线程能够获取到足够的令牌并继续运行吗?

答案:能,原因是release方法会添加令牌,并不会以初始化的大小为准

2、semaphore初始化有2个令牌,一个线程调用1次release方法,然后一次性获取3个令牌,会获取到吗?

答案:能,原因是release会添加令牌,并不会以初始化的大小为准。Semaphore中release方法的调用并没有限制要在acquire后调用。

3、semaphore初始化有10个令牌,11个线程同时各调用1次acquire方法,会发生什么?

答案:拿不到令牌的线程阻塞,不会继续往下运行。

4、semaphore初始化有10个令牌,一个线程重复调用11次acquire方法,会发生什么?

答案:线程阻塞,不会继续往下运行。可能你会考虑类似于锁的重入的问题,很好,但是,令牌没有重入的概念。你只要调用一次acquire方法,就需要有一个令牌才能继续运行

其中的内部逻辑及执行流程可参照JUC工具类: Semaphore详解 | Java 全栈知识体系

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值