信号量---Java Semaphore类详解说明


转自:

http://www.java265.com/JavaCourse/202204/3184.html

下文笔者讲述java中Semaphore类的详解说明,如下所示:

Semaphore简介

Semaphore(中文翻译:信号量)
   我们常用于控制访问某一资源的线程个数,
   使用这种方式可使大家按照一定的规则访问某一资源 
Semaphore的原理:
     通过使用计数器来控制对某一资源的访问
	  当计数器大于0,则允许访问
      当计数器为0时,则拒绝访问
      计数器中的计数作为允许访问共享资源的许可
即:访问资源,需从信号量中授予线程许可

Semaphore方法

方法名 备注
void acquire() 从信号量获取一个许可,在无可用许可前,将一直阻塞等待
void acquire(int permits) 获取指定数目的许可,在无可用许可前,也将会一直阻塞等待
boolean tryAcquire() 从信号量尝试获取一个许可,当无可用许可,直接返回false,不会阻塞
boolean tryAcquire(int permits) 尝试获取指定数目的许可,当无可用许可直接返回false
boolean tryAcquire(int permits, long timeout, TimeUnit unit) 在指定的时间内尝试从信号量中获取许可,当在指定的时间内获取成功,返回true,否则返回false
void release() 释放一个许可,别忘了在finally中使用
注意:多次调用该方法,会使信号量的许可数增加,达到动态扩展的效果
如:初始permits为1,调用了两次release,最大许可会改变为2
int availablePermits() 获取当前信号量可用的许可

Semaphore构造函数

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

public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }
参数说明:
     permits:初始许可数,即最大访问线程数
     fair:当设置为false时,创建的信号量为非公平锁;当设置为true时,信号量是公平锁

使用Semaphore限制登录的最大用户数

package com.java265.other;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class Test16 {
	public static void main(String[] args) {
		// 允许最大的获取用户的线程数
		int slots = 8;
		ExecutorService executorService = Executors.newFixedThreadPool(slots);
		UserUsingSemaphore loginQueue = new UserUsingSemaphore(slots);
		// 线程池模拟登录
		for (int i = 1; i <= slots; i++) {
			final int num = i;
			executorService.execute(() -> {
				if (loginQueue.tryAcquire()) {
					System.out.println("用户:" + num + "获取成功!");
				} else {
					System.out.println("用户:" + num + "获取失败!");
				}
			});
		}
		executorService.shutdown();

		System.out.println("当前可用许可证数:" + loginQueue.availableSlots());

		// 此时已经有8个线程,再次获取的时候会返回false
		if (loginQueue.tryAcquire()) {
			System.out.println("获取成功!");
		} else {
			System.out.println("资源已经被占满,获取失败!");
		}
		// 有用户释放资源,只释放一个资源
		loginQueue.releaseAcquire();

		// 再次获取
		if (loginQueue.tryAcquire()) {
			System.out.println("获取成功-2!");
		} else {
			System.out.println("资源已经被占满,获取失败-2!");
		}

		// 再次获取-会失败,因为又没有空余
		if (loginQueue.tryAcquire()) {
			System.out.println("获取成功-3!");
		} else {
			System.out.println("资源已经被占满,获取失败-3!");
		}
	}
}


/*
 * 定义一个登录队列,用于获取信号量
 */
class UserUsingSemaphore {

	private Semaphore semaphore;

	/**
	 * @param 设置信号量的大小
	 */
	public UserUsingSemaphore(int slotLimit) {
		semaphore = new Semaphore(slotLimit);
	}

	boolean tryAcquire() {
		// 获取一个凭证
		return semaphore.tryAcquire();
	}

	/*
	 * 释放凭证
	 */
	void releaseAcquire() {
		semaphore.release();
	}

	/*
	 * 获取可用的凭证数量
	 */
	int availableSlots() {
		return semaphore.availablePermits();
	}
}

------运行以上代码,将输出以下信息------
用户:1获取成功!
用户:5获取成功!
用户:2获取成功!
用户:4获取成功!
用户:3获取成功!
用户:7获取成功!
用户:6获取成功!
当前可用许可证数:1
资源已经被占满,获取失败!
获取成功-2!
资源已经被占满,获取失败-3!
用户:8获取成功!