并发编程常用工具类(二) SymaPhore实现线程池

1.symaPhore简介

symaphore(信号量)用来控制同时访问某个资源的线程数量,一般用在并发流量控制。个人对它的理解相当于是接待室每次只能接待固定数量的人,当达到最高接待数的时候,其他人就会被拦截在外等待,当前面接待完走出接待室,才会继续接待下面的人。

2.symaphore使用

symaphore有两个构造方法:构造方法Semaphore(int permits)接受一个int参数,表示可用的许可证数量,内部默认创建一个非公平锁;构造方法Semaphore(int permits, boolean fair)接受一个

int和一个boolean值,分别表示可用许可证数量和是否使用公平锁。(公平锁和非公平锁后面文章会单独提到)

一般在做流量控制的时候,我们就可以通过控制许可证数量来控制并发数的大小,接下来具体聊聊怎么实现对线程池的控制,代码如下:

 1 public class DBPoolSemaphore {
 2
 3     private final static int POOL_SIZE = 10;
 4     private final Semaphore useful,useless;//useful表示可用的数据库连接,useless表示已用的数据库连接
 5
 6     public DBPoolSemaphore() {
 7         this. useful = new Semaphore(POOL_SIZE);
 8         this.useless = new Semaphore(0);
 9     }
10
11     //存放数据库连接的容器
12     private static LinkedList<Connection> pool = new LinkedList<Connection>();
13     //初始化池
14     static {
15         for (int i = 0; i < POOL_SIZE; i++) {
16             pool.addLast(SqlConnectImpl.fetchConnection());
17         }
18     }
19
20     /*归还连接*/
21     public void returnConnect(Connection connection) throws InterruptedException {
22         if(connection!=null) {
23             System.out.println("当前有"+useful.getQueueLength()+"个线程等待数据库连接!!"
24                     +"可用连接数:"+useful.availablePermits());
25             useless.acquire();
26             synchronized (pool) {
27                 pool.addLast(connection);
28             }
29             useful.release();
30         }
31     }
32
33     /*从池子拿连接*/
34     public Connection takeConnect() throws InterruptedException {
35         useful.acquire();
36         Connection conn;
37         synchronized (pool) {
38             conn = pool.removeFirst();
39         }
40         useless.release();
41         return conn;
42     }
43 }

首先创建了两个symaphore对象,分别用来表示已用线程池和可用线程池,在设计拿连接和归还连接时,分别先后调用acquire()和release(),acquire()是用来获取许可,release()归还许可,相当于在拿连接时先去可用连接池获取许可,获取到才会继续执行,否则阻塞等待,直到有连接池归还了连接,可用线程许可中可以获取到,获取数据库连接,之后将不可用连接许可增加;归还连接刚好相反。本质就是通过控制可用和不可用许可数目,达到控制并发流量的效果。

下面是我设计的一段执行上面代码的示例:

 1 public class AppTest {
 2
 3     private static DBPoolSemaphore dbPool = new DBPoolSemaphore();
 4
 5     //业务线程
 6     private static class BusiThread extends Thread{
 7         @Override
 8         public void run() {
 9             Random r = new Random();//让每个线程持有连接的时间不一样
10             long start = System.currentTimeMillis();
11             try {
12                 Connection connect = dbPool.takeConnect();
13                 System.out.println("Thread_"+Thread.currentThread().getId()
14                         +"_获取数据库连接共耗时【"+(System.currentTimeMillis()-start)+"】ms.");
15                 SleepTools.ms(100+r.nextInt(100));//模拟业务操作,线程持有连接查询数据
16                 System.out.println("查询数据完成,归还连接!");
17                 dbPool.returnConnect(connect);
18             } catch (InterruptedException e) {
19             }
20         }
21     }
22
23     public static void main(String[] args) {
24         for (int i = 0; i < 50; i++) {
25             Thread thread = new BusiThread();
26             thread.start();
27         }
28     }
29
30 }

有兴趣的同学可以使用上述代码跑一下,可以看到前10个线程不耗时间,可以直接获取到,后面的会阻塞(花费时间越来越长),归还一个才会去获取连接,因为我的连接许可设置为10,所以每次最高并发数为10。

其他方法

Semaphore还提供一些其他方法:

  • int availablePermits() :返回此信号量中当前可用的许可证数。
  • int getQueueLength():返回正在等待获取许可证的线程数。
  • boolean hasQueuedThreads() :是否有线程正在等待获取许可证。
  • void reducePermits(int reduction) :减少reduction个许可证。是个protected方法。
  • Collection getQueuedThreads() :返回所有等待获取许可证的线程集合。是个protected方法。

ps:部分引用自http://ifeve.com/concurrency-semaphore/

原文地址:https://www.cnblogs.com/gmt-hao/p/9498526.html

时间: 2024-07-31 17:35:23

并发编程常用工具类(二) SymaPhore实现线程池的相关文章

并发编程常用工具类之countDownLatch和cyclicBarrier的使用对比

1.CountDownLatch           countDownLatch的作用是让一组线程等待其他线程完成工作以后在执行,相当于加强版的join(不懂可以百度一下join的用法),一般在初始化的时候会在构造方法传入计数器, 后续,在其他线程中每次调用countDown方法计数器减一,一般在需要等待的线程中调用countDownLatch的await方法阻塞线程,在当计数器为0时,等待线程继续运行. 光看上面的定义描述不是很直观,我们再来结合代码看一下实际运用: 1 public cla

Java并发编程原理与实战二十:线程安全性问题简单总结

一.出现线程安全性问题的条件 •在多线程的环境下 •必须有共享资源 •对共享资源进行非原子性操作 二.解决线程安全性问题的途径 •synchronized (偏向锁,轻量级锁,重量级锁) •volatile •JDK提供的原子类 •使用Lock(共享锁,排它锁) 三.认识的“*锁” •偏向锁 Java偏向锁(Biased Locking)是Java6引入的一项多线程优化. 偏向锁,顾名思义,它会偏向于第一个访问锁的线程,如果在运行过程中,同步锁只有一个线程访问,不存在多线程争用的情况,则线程是不

Java并发编程实践读书笔记(5) 线程池的使用

Executor与Task的耦合性 1,除非线程池很非常大,否则一个Task不要依赖同一个线程服务中的另外一个Task,因为这样容易造成死锁: 2,线程的执行是并行的,所以在设计Task的时候要考虑到线程安全问题.如果你认为只会在单任务线程的Executor中运行的话,从设计上讲这就已经耦合了. 3,长时间的任务有可能会影响到其他任务的执行效率,可以让其他线程在等待的时候限定一下等待时间.不要无限制地等待下去. 确定线程池的大小 给出如下定义: 要使CPU达到期望的使用率,线程池的大小应设置为:

Java并发编程原理与实战三十七:线程池的原理与使用

一.简介 线程池在我们的高并发环境下,实际应用是非常多的!!适用频率非常高! 有过使用过Executors框架的朋友,可能不太知道底层的实现,这里就是讲Executors是由ThreadPoolExecutor实现的.好的,让我们来看看ThreadPollExcutor是怎样实现的呢? 如果你想了解ThreadPoolExecutor的话.可以先从它的构造方法看起. ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime,

【转】Java并发编程:Thread类的使用

Java并发编程:Thread类的使用 Java并发编程:Thread类的使用 在前面2篇文章分别讲到了线程和进程的由来.以及如何在Java中怎么创建线程和进程.今天我们来学习一下Thread类,在学习Thread类之前,先介绍与线程相关知识:线程的几种状态.上下文切换,然后接着介绍Thread类中的方法的具体使用. 以下是本文的目录大纲: 一.线程的状态 二.上下文切换 三.Thread类中的方法 若有不正之处,请多多谅解并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http:/

js常用工具类.

一些js的工具类 复制代码 /** * Created by sevennight on 15-1-31. * js常用工具类 */ /** * 方法作用:[格式化时间] * 使用方法 * 示例: * 使用方式一: * var now = new Date(); * var nowStr = now.dateFormat("yyyy-MM-dd hh:mm:ss"); * 使用方式二: * new Date().dateFormat("yyyy年MM月dd日");

[C#] 常用工具类——直接在浏览器输出数据

/// <summary> /// <para> </para> /// 常用工具类——直接在浏览器输出数据 /// <para> -------------------------------------------------------------</para> /// <para> DumpDataTable:接在浏览器输出数据DataTable</para> /// <para> DumpListIt

java常用工具类(java技术交流群57388149)

package com.itjh.javaUtil; import java.util.ArrayList; import java.util.List; /** * * String工具类. <br> * * @author 宋立君 * @date 2014年06月24日 */ public class StringUtil { private static final int INDEX_NOT_FOUND = -1; private static final String EMPTY =

iOS开发常用工具类

iOS开发常用工具类(提高开发的工作效率) 前言 作为一个开发者应该学会去整理收集开发常用的工具类,这些复用的工具可以在项目开发中给你很大程度提高你的工作效率.难道你不想早点完成工作,然后出去撩妹.陪女朋友或者回家陪老婆孩子吗?反正我想早点回家??. 一.常用的宏定义 善于利用宏在开发中过程中会减少很多工作量比如定义开发过程中的常用尺寸,这样在后续开发中不用为了改一个相同尺寸而满世界的去找这个尺寸在哪用到了.宏定义用的很广泛,例如屏幕的宽高,网络请求的baseUrl等等下面是自己整理的一些示例: