JAVA并发编程——Semaphore(信号量)

控制并发线程数的Semaphore

Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。应用场景Semaphore可以用于做流量控制,特别是公用资源有限的应用场景,比如数据库连接。假如有一个需求,几十个线程并发地存储到数据库中,而数据库的连接数只有10个,这时我们必须控制只有10个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。这个时候,就可以使用Semaphore来做流量控制。

Semaphore的构造方法**Semaphore(int permits)**接受一个整型的数字,表示可用的许可证数量。Semaphore的用法也很简单,首先线程使用Semaphore的acquire()方法获取一个许可证,如果没有许可证,则阻塞。使用完之后调用release()方法归还许可证。还可以用tryAcquire()方法尝试获取许可证。

Semaphore还提供一些其他方法,具体如下:

intavailablePermits():返回此信号量中当前可用的许可证数。

intgetQueueLength():返回正在等待获取许可证的线程数。

booleanhasQueuedThreads():是否有线程正在等待获取许可证。

void reducePermits(int reduction):减少reduction个许可证,是个protected方法。

Collection getQueuedThreads():返回所有等待获取许可证的线程集合,是个protected方法。

实例代码

下面看一个数据库连接池的例子。演示Semaphore用法,一个数据库连接池的实现。

实现一个数据库连接,平庸实现,不做任何操作

public class SqlConnectImpl implements Connection{
	
	/*拿一个数据库连接*/
    public static final Connection fetchConnection(){
        return new SqlConnectImpl();
    }
//实现接口方法,但不做任操作
}

自定义数据连接池

public class DBPoolSemaphore {

	//数据库连接的数量
	private final static int POOL_SIZE = 10;
	//两个指示器,分别表示池子还有可用连接和已用连接
	private final Semaphore useful,useless;
	//存放数据库连接的容器
	private static LinkedList<Connection> pool = new LinkedList<>();
	//初始化池
	static {
        for (int i = 0; i < POOL_SIZE; i++) {
            pool.addLast(SqlConnectImpl.fetchConnection());
        }
	}
	//初始化许可证
	public DBPoolSemaphore() {
		this.useful = new Semaphore(10);
		this.useless = new Semaphore(0);
	}
	
	/*归还连接*/
	public void returnConnect(Connection connection) throws InterruptedException {
		if(connection!=null) {
			System.out.println("当前有"+useful.getQueueLength()+"个线程等待数据库连接!!"
					+"可用连接数:"+useful.availablePermits());
			//已用连接获取许可证
			useless.acquire();
			synchronized (pool) {
				pool.addLast(connection);
			}
			//可用连接
			useful.release();
		}
	}
	
	/*从池子拿连接*/
	public Connection takeConnect() throws InterruptedException {
		useful.acquire();
		Connection connection;
		synchronized (pool) {
			//从头上拿一个连接并返回
			connection = pool.removeFirst();
		}
		useless.release();
		return connection;
	}
	
}

测试数据库连接池

public class AppTest {

	private static DBPoolSemaphore dbPool = new DBPoolSemaphore();

	//拿数据库连接的线程
	private static class BusiThread extends Thread{
		@Override
		public void run() {
			Random r = new Random();//让每个线程持有连接的时间不一样
			long start = System.currentTimeMillis();
			try {
				Connection connect = dbPool.takeConnect();
				System.out.println("Thread_"+Thread.currentThread().getId()
						+"_获取数据库连接共耗时【"+(System.currentTimeMillis()-start)+"】ms.");
				SleepTools.ms(100+r.nextInt(100));//模拟业务操作,线程持有连接查询数据
				System.out.println("查询数据完成,归还连接!");
				dbPool.returnConnect(connect);
			} catch (InterruptedException e) {
			}
		}
	}
	
	public static void main(String[] args) {
        for (int i = 0; i < 50; i++) {
            Thread thread = new BusiThread();
            thread.start();
        }
	}
	
}

结果如下:

Thread_19_获取数据库连接共耗时【0】ms.
Thread_23_获取数据库连接共耗时【0】ms.
Thread_12_获取数据库连接共耗时【0】ms.
Thread_14_获取数据库连接共耗时【0】ms.
Thread_18_获取数据库连接共耗时【0】ms.
Thread_20_获取数据库连接共耗时【0】ms.
Thread_16_获取数据库连接共耗时【0】ms.
Thread_17_获取数据库连接共耗时【0】ms.
Thread_13_获取数据库连接共耗时【0】ms.
Thread_15_获取数据库连接共耗时【0】ms.
查询数据完成,归还连接!
当前有40个线程等待数据库连接!!可用连接数:0
Thread_22_获取数据库连接共耗时【154】ms.
查询数据完成,归还连接!
当前有39个线程等待数据库连接!!可用连接数:0
Thread_21_获取数据库连接共耗时【163】ms.
查询数据完成,归还连接!
当前有38个线程等待数据库连接!!可用连接数:0
Thread_25_获取数据库连接共耗时【193】ms.
查询数据完成,归还连接!
当前有37个线程等待数据库连接!!可用连接数:0
Thread_24_获取数据库连接共耗时【195】ms.
查询数据完成,归还连接!
当前有36个线程等待数据库连接!!可用连接数:0
Thread_26_获取数据库连接共耗时【198】ms.
查询数据完成,归还连接!
当前有35个线程等待数据库连接!!可用连接数:0
Thread_27_获取数据库连接共耗时【210】ms.
查询数据完成,归还连接!
当前有34个线程等待数据库连接!!可用连接数:0
Thread_28_获取数据库连接共耗时【211】ms.
查询数据完成,归还连接!
当前有33个线程等待数据库连接!!可用连接数:0
Thread_30_获取数据库连接共耗时【225】ms.
查询数据完成,归还连接!
当前有32个线程等待数据库连接!!可用连接数:0
Thread_31_获取数据库连接共耗时【237】ms.
查询数据完成,归还连接!
当前有31个线程等待数据库连接!!可用连接数:0
Thread_32_获取数据库连接共耗时【247】ms.
查询数据完成,归还连接!
当前有30个线程等待数据库连接!!可用连接数:0
Thread_34_获取数据库连接共耗时【276】ms.
查询数据完成,归还连接!
当前有29个线程等待数据库连接!!可用连接数:0
Thread_33_获取数据库连接共耗时【316】ms.
查询数据完成,归还连接!
当前有28个线程等待数据库连接!!可用连接数:0
Thread_36_获取数据库连接共耗时【335】ms.
查询数据完成,归还连接!
当前有27个线程等待数据库连接!!可用连接数:0
Thread_35_获取数据库连接共耗时【336】ms.
查询数据完成,归还连接!
当前有26个线程等待数据库连接!!可用连接数:0
Thread_37_获取数据库连接共耗时【347】ms.
查询数据完成,归还连接!
当前有25个线程等待数据库连接!!可用连接数:0
Thread_38_获取数据库连接共耗时【359】ms.
查询数据完成,归还连接!
当前有24个线程等待数据库连接!!可用连接数:0
Thread_39_获取数据库连接共耗时【369】ms.
查询数据完成,归还连接!
当前有23个线程等待数据库连接!!可用连接数:0
Thread_40_获取数据库连接共耗时【381】ms.
查询数据完成,归还连接!
当前有22个线程等待数据库连接!!可用连接数:0
Thread_41_获取数据库连接共耗时【401】ms.
查询数据完成,归还连接!
当前有21个线程等待数据库连接!!可用连接数:0
Thread_42_获取数据库连接共耗时【404】ms.
查询数据完成,归还连接!
当前有20个线程等待数据库连接!!可用连接数:0
Thread_43_获取数据库连接共耗时【446】ms.
查询数据完成,归还连接!
当前有19个线程等待数据库连接!!可用连接数:0
Thread_44_获取数据库连接共耗时【459】ms.
查询数据完成,归还连接!
当前有18个线程等待数据库连接!!可用连接数:0
Thread_45_获取数据库连接共耗时【479】ms.
查询数据完成,归还连接!
当前有17个线程等待数据库连接!!可用连接数:0
Thread_46_获取数据库连接共耗时【509】ms.
查询数据完成,归还连接!
当前有16个线程等待数据库连接!!可用连接数:0
查询数据完成,归还连接!
Thread_47_获取数据库连接共耗时【520】ms.
当前有15个线程等待数据库连接!!可用连接数:0
Thread_48_获取数据库连接共耗时【521】ms.
查询数据完成,归还连接!
当前有14个线程等待数据库连接!!可用连接数:0
Thread_49_获取数据库连接共耗时【533】ms.
查询数据完成,归还连接!
当前有13个线程等待数据库连接!!可用连接数:0
Thread_50_获取数据库连接共耗时【542】ms.
查询数据完成,归还连接!
查询数据完成,归还连接!
当前有12个线程等待数据库连接!!可用连接数:0
当前有12个线程等待数据库连接!!可用连接数:0
Thread_51_获取数据库连接共耗时【557】ms.
Thread_52_获取数据库连接共耗时【557】ms.
查询数据完成,归还连接!
当前有10个线程等待数据库连接!!可用连接数:0
Thread_53_获取数据库连接共耗时【564】ms.
查询数据完成,归还连接!
当前有9个线程等待数据库连接!!可用连接数:0
Thread_54_获取数据库连接共耗时【599】ms.
查询数据完成,归还连接!
当前有8个线程等待数据库连接!!可用连接数:0
Thread_55_获取数据库连接共耗时【613】ms.
查询数据完成,归还连接!
当前有7个线程等待数据库连接!!可用连接数:0
Thread_56_获取数据库连接共耗时【629】ms.
查询数据完成,归还连接!
当前有6个线程等待数据库连接!!可用连接数:0
Thread_57_获取数据库连接共耗时【635】ms.
查询数据完成,归还连接!
当前有5个线程等待数据库连接!!可用连接数:0
Thread_58_获取数据库连接共耗时【654】ms.
查询数据完成,归还连接!
当前有4个线程等待数据库连接!!可用连接数:0
Thread_59_获取数据库连接共耗时【672】ms.
查询数据完成,归还连接!
当前有3个线程等待数据库连接!!可用连接数:0
Thread_60_获取数据库连接共耗时【697】ms.
查询数据完成,归还连接!
当前有2个线程等待数据库连接!!可用连接数:0
Thread_61_获取数据库连接共耗时【709】ms.
查询数据完成,归还连接!
当前有1个线程等待数据库连接!!可用连接数:0
Thread_29_获取数据库连接共耗时【717】ms.
查询数据完成,归还连接!
当前有0个线程等待数据库连接!!可用连接数:0
查询数据完成,归还连接!
当前有0个线程等待数据库连接!!可用连接数:1
查询数据完成,归还连接!
当前有0个线程等待数据库连接!!可用连接数:2
查询数据完成,归还连接!
当前有0个线程等待数据库连接!!可用连接数:3
查询数据完成,归还连接!
当前有0个线程等待数据库连接!!可用连接数:4
查询数据完成,归还连接!
当前有0个线程等待数据库连接!!可用连接数:5
查询数据完成,归还连接!
当前有0个线程等待数据库连接!!可用连接数:6
查询数据完成,归还连接!
当前有0个线程等待数据库连接!!可用连接数:7
查询数据完成,归还连接!
当前有0个线程等待数据库连接!!可用连接数:8
查询数据完成,归还连接!
当前有0个线程等待数据库连接!!可用连接数:9

最开始的线程获取数据连的接耗时为0,随着线程的递减,耗时呈递增状态,最后都获取到了数据连接 ,最后获取到数据连接的10个线程归还线程。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值