博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 并发专题 : Semaphore 实现 互斥 与 连接池
阅读量:4873 次
发布时间:2019-06-11

本文共 4102 字,大约阅读时间需要 13 分钟。

继续并发方面的知识。今天介绍Semaphore,同样在java.util.concurrent包下。

本来准备通过例子,从自己实现到最后使用并发工具实现,但是貌似效果并不是很好,有点太啰嗦的感觉,所有准备直入主题。

介绍:Semaphore中管理着一组虚拟的许可,许可的初始数量可通过构造函数来指定【new Semaphore(1);】,执行操作时可以首先获得许可【semaphore.acquire();】,并在使用后释放许可【semaphore.release();】。如果没有许可,那么acquire方法将会一直阻塞直到有许可(或者直到被终端或者操作超时)。

作用:可以用来控制同时访问某个特定资源的操作数量,或者某个操作的数量。

下面使用Semaphore实现两个例子:

1、互斥

大家都学过操作系统,都知道互斥的概念,比较简单的互斥实现,比如PV操作,判断资源,然后忙等实现互斥;上一篇博客也说过,忙等对CPU的消耗巨大,下面我们通过Semaphore来实现一个比较好的互斥操作:

假设我们公司只有一台打印机,我们需要对这台打印机的打印操作进行互斥控制:

package com.zhy.concurrency.semaphore;import java.util.concurrent.Semaphore;/** * 使用信号量机制,实现互斥访问打印机 *  * @author zhy *  */public class MutexPrint{	/**	 * 定义初始值为1的信号量	 */	private final Semaphore semaphore = new Semaphore(1);	/**	 * 模拟打印操作	 * @param str	 * @throws InterruptedException	 */	public void print(String str) throws InterruptedException	{		//请求许可		semaphore.acquire();				System.out.println(Thread.currentThread().getName()+" enter ...");		Thread.sleep(1000);		System.out.println(Thread.currentThread().getName() + "正在打印 ..." + str);		System.out.println(Thread.currentThread().getName()+" out ...");		//释放许可		semaphore.release();	}	public static void main(String[] args)	{		final MutexPrint print = new MutexPrint();		/**		 * 开启10个线程,抢占打印机		 */		for (int i = 0; i < 10; i++)		{			new Thread()			{				public void run()				{					try					{						print.print("helloworld");					} catch (InterruptedException e)					{						e.printStackTrace();					}				};			}.start();		}	}}
输出结果:

Thread-1 enter ...Thread-1正在打印 ...helloworldThread-1 out ...Thread-2 enter ...Thread-2正在打印 ...helloworldThread-2 out ...Thread-0 enter ...Thread-0正在打印 ...helloworldThread-0 out ...Thread-3 enter ...Thread-3正在打印 ...helloworldThread-3 out ...
通过初始值为1的Semaphore,很好的实现了资源的互斥访问。

2、连接池的模拟实现

在项目中处理高并发时,一般数据库都会使用数据库连接池,假设现在数据库连接池最大连接数为10,当10个连接都分配出去以后,现在有用户继续请求连接,可能的处理:

a、手动抛出异常,用户界面显示,服务器忙,稍后再试

b、阻塞,等待其他连接的释放

从用户体验上来说,更好的选择当然是阻塞,等待其他连接的释放,用户只会觉得稍微慢了一点,并不影响他的操作。下面使用Semaphore模拟实现一个数据库连接池:

package com.zhy.concurrency.semaphore;import java.util.ArrayList;import java.util.List;import java.util.concurrent.Semaphore;/** * 使用Semaphore模拟数据库链接池的使用 * @author zhy * */public class ConnectPool{	private final List
pool = new ArrayList
(3); private final Semaphore semaphore = new Semaphore(3); /** * 初始化分配3个连接 */ public ConnectPool() { pool.add(new Conn()); pool.add(new Conn()); pool.add(new Conn()); } /** * 请求分配连接 * @return * @throws InterruptedException */ public Conn getConn() throws InterruptedException { semaphore.acquire(); Conn c = null ; synchronized (pool) { c = pool.remove(0); } System.out.println(Thread.currentThread().getName()+" get a conn " + c); return c ; } /** * 释放连接 * @param c */ public void release(Conn c) { pool.add(c); System.out.println(Thread.currentThread().getName()+" release a conn " + c); semaphore.release(); } public static void main(String[] args) { final ConnectPool pool = new ConnectPool(); /** * 第一个线程占用1个连接3秒 */ new Thread() { public void run() { try { Conn c = pool.getConn(); Thread.sleep(3000); pool.release(c); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); /** * 开启3个线程请求分配连接 */ for (int i = 0; i < 3; i++) { new Thread() { public void run() { try { Conn c = pool.getConn(); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); } } private class Conn { }}

Thread-0 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@12b6651Thread-2 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@e53108Thread-1 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@1888759Thread-0 release a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@12b6651Thread-3 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@12b6651
我们测试时,让Thread-0持有一个连接3秒,然后瞬间让3个线程再去请求分配连接,造成Thread-3一直等到Thread-0对连接的释放,然后获得连接。

通过两个例子,基本已经了解了Semaphore的用法,这里的线程池例子只是为了说明Semaphore的用法,真实的实现代码比这复杂的多,而且可能也不会直接用Semaphore。

好了,之后会继续Java并发的博客。

版权声明:本文为博主原创文章,未经博主允许不得转载。

转载于:https://www.cnblogs.com/dingxiaoyue/p/4924952.html

你可能感兴趣的文章
grunt对象之api
查看>>
《驻足思考》笔记
查看>>
全网最详细的Windows系统里PLSQL Developer 64bit的下载与安装过程(图文详解)
查看>>
Spark MLlib回归算法LinearRegression
查看>>
Hadoop MapReduce概念学习系列之mr的Shuffle(二十二)
查看>>
away3D案例3
查看>>
好的git教程
查看>>
Delphi XE增强的RTTI妙用--动态创建包中的窗口类
查看>>
unisynedit 在Delphi 2010下的编译问题
查看>>
mssql 动态行转列。
查看>>
工作杂记
查看>>
老话题,权限设计及实现!
查看>>
POJ 2393 贪心 简单题
查看>>
3. Quartz2D 绘制矩形、圆形、弧形
查看>>
latex 学习笔记
查看>>
SQL 数据库 学习 005 学习必备的一些操作 --- 如何新建数据库 如何附加和分离数据库(如何备份还原数据库) 如何删除数据库...
查看>>
[php排错] Forbidden You don't have permission to access / on this server.
查看>>
MVC中的helper标签
查看>>
Spring Cloud Gateway入门
查看>>
XCode 4.2 新功能 - Storyboard
查看>>