Java中Semaphore(信号量) 数据库连接池
计数信号量用来控制同时访问某个特定资源的操作数或同时执行某个指定操作的数量
A counting semaphore.Conceptually, a semaphore maintains a set of permits. Each acquire blocks if necessary until a permit is available, and then takes it. Each release adds a permit, potentially releasing a blocking acquirer. However, no actual permit objects are used; the Semaphore just keeps a count of the number available and acts accordingly.
从概念上来说,Semaphore中维护了一组许可,许可的数量在构造函数中指定。acquire方法将获取一个可用的许可,如果没有可用的许可,该方法会被阻塞,直到Semaphore中有可用的许可。release方法释放一个许可,如果此时存在阻塞中的acqure方法,将释放一个阻塞中的acquire
事实上,Semaphore中只维护可用请求数量,并不包含实际的请求对象
示例一:数据库连接池
在初始化Semaphore时可以设置其公平性,如果为公平Semaphore,则按照请求时间获得许可,即先发送的请求先获得许可,如果为非公平Semaphore,则先发送的请求未必先获得许可,这有助于提高程序的吞吐量,但是有可能导致某些请求始终获取不到许可(tryAcquire方法不使用公平性设置)
- import java.sql.Connection;
- import java.sql.DriverManager;
- import java.util.HashMap;
- import java.util.LinkedList;
- import java.util.Map;
- import java.util.concurrent.Semaphore;
- public class MyConnPool {
- private LinkedList<Connection> unusedConns =
- new LinkedList<Connection>();
- //释放连接时对查找性能要求较高,故使用哈希表
- private Map<Connection,String> usedConns =
- new HashMap<Connection,String>();
- private final Semaphore available;
- public MyConnPool(int size) throws Exception{
- StringBuilder builder = new StringBuilder();
- builder.append("-----pool-----\n");
- available = new Semaphore(size, true);//公平性Semaphore
- String url = "jdbc:mysql://ip:port/name?user=user&password=pwd";
- for(int i = 0 ; i < size ; i++){
- Connection conn = DriverManager.getConnection(url);
- unusedConns.add(conn);
- builder.append("conn-" + i + ":" + conn.hashCode() + "\n");
- }
- builder.append("--------------\n");
- System.out.print(builder.toString());
- }
- public Connection getConn() throws InterruptedException{
- //获取Semaphore中的许可
- available.acquire();
- Connection conn = null;
- synchronized(this){
- conn = unusedConns.removeFirst();
- usedConns.put(conn, "");
- System.out.println(Thread.currentThread().getName()
- + ":" + conn.hashCode() + "[got]");
- System.out.println(display());
- }
- return conn;
- }
- public void close(Connection conn){
- synchronized(this){
- if(usedConns.containsKey(conn)){
- usedConns.remove(conn);
- unusedConns.addLast(conn);
- System.out.println(Thread.currentThread().getName()
- + ":" + conn.hashCode() + "[closed]");
- System.out.println(display());
- }
- }
- //释放线程获取的许可
- available.release();
- }
- private final synchronized String display(){
- String str = "";
- if(unusedConns.size() > 0){
- str = "";
- for(Connection conn : unusedConns){
- str += conn.hashCode() + "|";
- }
- }
- if(!str.equals(""))
- return str;
- else
- return "empty";
- }
- }
- import java.sql.Connection;
- import java.util.concurrent.CountDownLatch;
- public class Test implements Runnable{
- private static CountDownLatch latch
- = new CountDownLatch(1);
- private MyConnPool pool;
- public Test(MyConnPool pool){
- this.pool = pool;
- }
- @Override
- public void run(){
- try {
- latch.await();
- Connection conn = pool.getConn();
- Thread.sleep(1*1000);
- pool.close(conn);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- public static void main(String[] args) throws Exception{
- MyConnPool pool = new MyConnPool(2);
- for(int i = 0 ; i < 4 ; i++){
- Thread t = new Thread(new Test(pool));
- t.start();
- }
- //保证4个线程同时运行
- latch.countDown();
- }
- }
运行结果如下:
- -----pool-----
- conn-0:11631043
- conn-1:14872264
- --------------
- Thread-4:11631043[got]
- 14872264|
- Thread-1:14872264[got]
- empty
- Thread-4:11631043[closed]
- 11631043|
- Thread-2:11631043[got]
- empty
- Thread-1:14872264[closed]
- 14872264|
- Thread-3:14872264[got]
- empty
- Thread-2:11631043[closed]
- 11631043|
- Thread-3:14872264[closed]
- 11631043|14872264|
特别注意如果getConn方法和close方法都为同步方法,将产生死锁:
- public synchronized Connection getConn() throws InterruptedException{
- ......
- }
- public synchronized void close(Connection conn){
- ......
- }
同一时刻只能有一个线程调用连接池的getConn方法或close方法,当Semaphore中没有可用的许可,并且此时恰好有一个线程成功调用连接池的getConn方法,则该线程将一直阻塞在acquire方法上,其它线程将没有办法获取连接池上的锁并调用close方法释放许可,程序将会卡死
阻塞方法上不要加锁,否则将导致锁长时间不释放,如果该锁为互斥锁,将导致程序卡住
acquire方法本身使用乐观锁实现,也不需要再加互斥锁
示例二:不可重入互斥锁
- import java.util.concurrent.CountDownLatch;
- import java.util.concurrent.Semaphore;
- public class Test implements Runnable{
- private static CountDownLatch latch =
- new CountDownLatch(1);
- private static Semaphore lock =
- new Semaphore(1, true);
- @Override
- public void run(){
- try {
- latch.await();
- this.work();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- private void work() throws InterruptedException{
- lock.acquire();
- System.out.println("Locking by "
- + Thread.currentThread().getName());
- Thread.sleep(1*1000);
- lock.release();
- }
- public static void main(String[] args) throws Exception{
- for(int i = 0 ; i < 4 ; i++){
- Thread t = new Thread(new Test());
- t.start();
- }
- //保证4个线程同时运行
- latch.countDown();
- }
- }
运行结果如下:
- Locking by Thread-3
- Locking by Thread-0
- Locking by Thread-1
- Locking by Thread-2
Java中Semaphore(信号量) 数据库连接池的更多相关文章
- Java中Semaphore(信号量)的使用
Semaphore的作用: 在java中,使用了synchronized关键字和Lock锁实现了资源的并发访问控制,在同一时间只允许唯一了线程进入临界区访问资源(读锁除外),这样子控制的主要目的是为了 ...
- java中 几种数据库连接池 的写法
JDBC连接数据库 •创建一个以JDBC连接数据库的程序,包含7个步骤: 1.加载JDBC驱动程序: 在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Java虚拟机), 这通过java.l ...
- Java中的BoneCP数据库连接池用法
http://zhoufoxcn.blog.51cto.com/792419/438277/ C3P0,Proxool,BoneCP,Druid
- 转载:Java中的字符串常量池详细介绍
引用自:http://blog.csdn.net/langhong8/article/details/50938041 这篇文章主要介绍了Java中的字符串常量池详细介绍,JVM为了减少字符串对象的重 ...
- Java中的字符串常量池,栈和堆的概念
问题:String str = new String(“abc”),“abc”在内存中是怎么分配的? 答案是:堆内存.(Tips:jdk1.8 已经将字符串常量池放在堆内存区) 题目考查的为Ja ...
- java中的信号量Semaphore
Semaphore(信号量)充当了操作系统概念下的“信号量”.它提供了“临界区中可用资源信号量”的相同功能.以一个停车场运作为例.为了简单起见,假设停车场只有三个车位,一开始三个车位都是空的.这时如果 ...
- Java 中 Semaphore 是什么?
Java 中的 Semaphore 是一种新的同步类,它是一个计数信号.从概念上讲,从 概念上讲,信号量维护了一个许可集合.如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可.每 ...
- 【Java进阶】——初识数据库连接池
[简介] 数据库连接池:程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态地对池中的链接进行申请,使用,释放. 相比之前的程序连接,减少了数据库的打开关闭次数,从而减少了程序响应的 ...
- JAVA中事物以及连接池
一.事物 什么是事物? 事务,一般是指要做的或所做的事情.在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元.这些单元要么全都成功,要么全都不成功. 做一件事情,这个一件事情中有多个 ...
随机推荐
- TOT 傅立叶变换 FFT 入门
HDU 1402,计算很大的两个数相乘. FFT 只要78ms,这里: 一些FFT 入门资料:http://wenku.baidu.com/view/8bfb0bd476a20029bd642d85. ...
- Spring 详解(二)------- AOP关键概念以及两种实现方式
目录 1. AOP 关键词 2. AOP 的作用 3. AOP 的通知类型 4. 基于 xml 的配置方式 5. 基于注解的配置方式 6. 切面的优先级 7. 重用切点表达式 8. 两种方式的比较(摘 ...
- python 怎么启动一个外部命令程序, 并且不阻塞当前进程
http://www.myexception.cn/perl-python/1278887.html http://blog.chinaunix.net/uid-25979788-id-3081912 ...
- 一种排序(nyoj8)(简单排序)
一种排序 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描写叙述 如今有非常多长方形.每个长方形都有一个编号,这个编号能够反复.还知道这个长方形的宽和长,编号.长.宽都是整数 ...
- Ghost本地安装highlight.js使代码高亮
对于程序猿写博客来说,这代码高亮是起码的要求.可是Ghost本身没有支持高亮代码. 可是能够通过扩展来实现,它就是highlight.js--附官方站点,看了下首页介绍,真的非常强大,如今说说怎么进行 ...
- 向量空间模型实现文档查询(Vector Space Model to realize document query)
xml中文档(query)的结构: <topic> <number>CIRB010TopicZH006</number> <title>科索沃難民潮&l ...
- C#语言基础语句
case,switch,break的使用 Console.WriteLine("1.汉堡"); Console.WriteLine("2.薯条"); Conso ...
- 【转载】.NET Remoting学习笔记(一)概念
目录 .NET Remoting学习笔记(一)概念 .NET Remoting学习笔记(二)激活方式 .NET Remoting学习笔记(三)信道 背景 自接触编程以来,一直听过这个名词Remotin ...
- jk_proxy实现apache+tomcat负载均衡
Apache + tomcat实现server集群 主要參照:http://blog.csdn.net/welun521/article/details/4169879 watermark/2/tex ...
- ribbon负载均衡进行服务消费
相同服务以不同端口形式注册到eureka上,ribbon从eureka上获取冰进行服务消费,会偶现如下现象: I/O error on GET request for "http://COM ...