计数信号量用来控制同时访问某个特定资源的操作数或同时执行某个指定操作的数量

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方法不使用公平性设置)

  1. import java.sql.Connection;
  2. import java.sql.DriverManager;
  3. import java.util.HashMap;
  4. import java.util.LinkedList;
  5. import java.util.Map;
  6. import java.util.concurrent.Semaphore;
  7. public class MyConnPool {
  8. private LinkedList<Connection> unusedConns =
  9. new LinkedList<Connection>();
  10. //释放连接时对查找性能要求较高,故使用哈希表
  11. private Map<Connection,String> usedConns =
  12. new HashMap<Connection,String>();
  13. private final Semaphore available;
  14. public MyConnPool(int size) throws Exception{
  15. StringBuilder builder = new StringBuilder();
  16. builder.append("-----pool-----\n");
  17. available = new Semaphore(size, true);//公平性Semaphore
  18. String url = "jdbc:mysql://ip:port/name?user=user&password=pwd";
  19. for(int i = 0 ; i < size ; i++){
  20. Connection conn = DriverManager.getConnection(url);
  21. unusedConns.add(conn);
  22. builder.append("conn-" + i + ":" + conn.hashCode() + "\n");
  23. }
  24. builder.append("--------------\n");
  25. System.out.print(builder.toString());
  26. }
  27. public Connection getConn() throws InterruptedException{
  28. //获取Semaphore中的许可
  29. available.acquire();
  30. Connection conn = null;
  31. synchronized(this){
  32. conn = unusedConns.removeFirst();
  33. usedConns.put(conn, "");
  34. System.out.println(Thread.currentThread().getName()
  35. + ":" + conn.hashCode() + "[got]");
  36. System.out.println(display());
  37. }
  38. return conn;
  39. }
  40. public void close(Connection conn){
  41. synchronized(this){
  42. if(usedConns.containsKey(conn)){
  43. usedConns.remove(conn);
  44. unusedConns.addLast(conn);
  45. System.out.println(Thread.currentThread().getName()
  46. + ":" + conn.hashCode() + "[closed]");
  47. System.out.println(display());
  48. }
  49. }
  50. //释放线程获取的许可
  51. available.release();
  52. }
  53. private final synchronized String display(){
  54. String str = "";
  55. if(unusedConns.size() > 0){
  56. str = "";
  57. for(Connection conn : unusedConns){
  58. str += conn.hashCode() + "|";
  59. }
  60. }
  61. if(!str.equals(""))
  62. return str;
  63. else
  64. return "empty";
  65. }
  66. }
  1. import java.sql.Connection;
  2. import java.util.concurrent.CountDownLatch;
  3. public class Test implements Runnable{
  4. private static CountDownLatch latch
  5. = new CountDownLatch(1);
  6. private MyConnPool pool;
  7. public Test(MyConnPool pool){
  8. this.pool = pool;
  9. }
  10. @Override
  11. public void run(){
  12. try {
  13. latch.await();
  14. Connection conn = pool.getConn();
  15. Thread.sleep(1*1000);
  16. pool.close(conn);
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. public static void main(String[] args) throws Exception{
  22. MyConnPool pool = new MyConnPool(2);
  23. for(int i = 0 ; i < 4 ; i++){
  24. Thread t = new Thread(new Test(pool));
  25. t.start();
  26. }
  27. //保证4个线程同时运行
  28. latch.countDown();
  29. }
  30. }

运行结果如下:

  1. -----pool-----
  2. conn-0:11631043
  3. conn-1:14872264
  4. --------------
  5. Thread-4:11631043[got]
  6. 14872264|
  7. Thread-1:14872264[got]
  8. empty
  9. Thread-4:11631043[closed]
  10. 11631043|
  11. Thread-2:11631043[got]
  12. empty
  13. Thread-1:14872264[closed]
  14. 14872264|
  15. Thread-3:14872264[got]
  16. empty
  17. Thread-2:11631043[closed]
  18. 11631043|
  19. Thread-3:14872264[closed]
  20. 11631043|14872264|

特别注意如果getConn方法和close方法都为同步方法,将产生死锁:

  1. public synchronized Connection getConn() throws InterruptedException{
  2. ......
  3. }
  4. public synchronized void close(Connection conn){
  5. ......
  6. }

同一时刻只能有一个线程调用连接池的getConn方法或close方法,当Semaphore中没有可用的许可,并且此时恰好有一个线程成功调用连接池的getConn方法,则该线程将一直阻塞在acquire方法上,其它线程将没有办法获取连接池上的锁并调用close方法释放许可,程序将会卡死

阻塞方法上不要加锁,否则将导致锁长时间不释放,如果该锁为互斥锁,将导致程序卡住

acquire方法本身使用乐观锁实现,也不需要再加互斥锁

示例二:不可重入互斥锁

  1. import java.util.concurrent.CountDownLatch;
  2. import java.util.concurrent.Semaphore;
  3. public class Test implements Runnable{
  4. private static CountDownLatch latch =
  5. new CountDownLatch(1);
  6. private static Semaphore lock =
  7. new Semaphore(1, true);
  8. @Override
  9. public void run(){
  10. try {
  11. latch.await();
  12. this.work();
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. }
  17. private void work() throws InterruptedException{
  18. lock.acquire();
  19. System.out.println("Locking by "
  20. + Thread.currentThread().getName());
  21. Thread.sleep(1*1000);
  22. lock.release();
  23. }
  24. public static void main(String[] args) throws Exception{
  25. for(int i = 0 ; i < 4 ; i++){
  26. Thread t = new Thread(new Test());
  27. t.start();
  28. }
  29. //保证4个线程同时运行
  30. latch.countDown();
  31. }
  32. }

运行结果如下:

  1. Locking by Thread-3
  2. Locking by Thread-0
  3. Locking by Thread-1
  4. Locking by Thread-2
 
 

Java中Semaphore(信号量) 数据库连接池的更多相关文章

  1. Java中Semaphore(信号量)的使用

    Semaphore的作用: 在java中,使用了synchronized关键字和Lock锁实现了资源的并发访问控制,在同一时间只允许唯一了线程进入临界区访问资源(读锁除外),这样子控制的主要目的是为了 ...

  2. java中 几种数据库连接池 的写法

    JDBC连接数据库 •创建一个以JDBC连接数据库的程序,包含7个步骤: 1.加载JDBC驱动程序: 在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Java虚拟机), 这通过java.l ...

  3. Java中的BoneCP数据库连接池用法

    http://zhoufoxcn.blog.51cto.com/792419/438277/ C3P0,Proxool,BoneCP,Druid

  4. 转载:Java中的字符串常量池详细介绍

    引用自:http://blog.csdn.net/langhong8/article/details/50938041 这篇文章主要介绍了Java中的字符串常量池详细介绍,JVM为了减少字符串对象的重 ...

  5. Java中的字符串常量池,栈和堆的概念

    问题:String str = new String(“abc”),“abc”在内存中是怎么分配的?    答案是:堆内存.(Tips:jdk1.8 已经将字符串常量池放在堆内存区) 题目考查的为Ja ...

  6. java中的信号量Semaphore

    Semaphore(信号量)充当了操作系统概念下的“信号量”.它提供了“临界区中可用资源信号量”的相同功能.以一个停车场运作为例.为了简单起见,假设停车场只有三个车位,一开始三个车位都是空的.这时如果 ...

  7. Java 中 Semaphore 是什么?

    Java 中的 Semaphore 是一种新的同步类,它是一个计数信号.从概念上讲,从 概念上讲,信号量维护了一个许可集合.如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可.每 ...

  8. 【Java进阶】——初识数据库连接池

    [简介] 数据库连接池:程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态地对池中的链接进行申请,使用,释放. 相比之前的程序连接,减少了数据库的打开关闭次数,从而减少了程序响应的 ...

  9. JAVA中事物以及连接池

    一.事物 什么是事物? 事务,一般是指要做的或所做的事情.在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元.这些单元要么全都成功,要么全都不成功. 做一件事情,这个一件事情中有多个 ...

随机推荐

  1. [NOIP2012T3]开车旅行

    题目描述 NOIP 2012 提高组 题3小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 ...

  2. Java ListIterator 与 Iterator 异同

    一.概述 基于 fail-fast 机制,我们知道对于ArrayList等集合在迭代过程中是不可进行结构修改操作的,唯一能使用的结构修改操作只有Iterator接口中的remove()方法. 而jav ...

  3. Maven用项目模板生成项目

    Archetype插件是Maven生成项目的项目模板,项目模板就是一个框,把文件夹这些定好,然后就可以在上开写具体逻辑等等. 最常用的命令: mvn archetype:generate 这个是生成一 ...

  4. iOS 内存管理实践

    内存管理实践 尽管基本的概念在内存管理策略文章中简单得阐述了,但是还有一些实用的步骤让你更容易管理内存:有助于确保你的程序最大限度地减少资源需求的同时,保持可靠和强大. 使用“访问器方法”让内存管理更 ...

  5. centos下开启htaccess

    不知道原本 centOS是否默认支持 .htaccess 可能是因为我总弄配置文件无意中给搞坏了 今天要用到就查了下怎么开启 想要顺利开启需注意以下几点, 这几点都是在httpd.conf 这个配置文 ...

  6. 最大熵推导LR

    http://www.win-vector.com/dfiles/LogisticRegressionMaxEnt.pdf https://www.zhihu.com/question/2409455 ...

  7. GoogLeNet系列解读

    GoogLeNet Incepetion V1 这是GoogLeNet的最早版本,出现在2014年的<Going deeper with convolutions>.之所以名为“GoogL ...

  8. BUPT 2012复试机考 4T

    题目描述 我们都学习过计算机网络,知道网络层IP协议数据包的头部格式如下: 其中IHL表示IP头的长度,单位是4字节:总长表示整个数据包的长度,单位是1字节.传输层的TCP协议数据段的头部格式如下:  ...

  9. 高速清除winXP系统中explorer.exe病毒

    关于这个explorer.exe病毒.是眼下xp最为常见的一个病毒,会大量的消耗系统资源,造成电脑特别的卡顿. 1.关闭还原(假设没有,则跳过),为的是防止我们改动后,还原之后又回来了. 2.打开注冊 ...

  10. 我的javascript

    JavaScript的简介: 1.定义:javascript是一门动态弱类型的解释型编程语言,增强页面动态效果,实现页面与用户之间的实时动态的交互. javascript是由三部分组成:ECMAScr ...