并发编程线程池
参考资料
大牛博客:宏观上分析 线程池
http://ifeve.com/java-threadpool/
CSDN系列专栏简易
http://blog.csdn.net/column/details/javathreadpool.html
死磕java并发系列有4篇线程池相关的
http://cmsblogs.com/?p=2122
类的继承关系
- Executor作为顶层接口,只提供一个execute()接口方法
- ThreadPoolExecutor继承AbstractExecutorService抽象类–>ExcutorService接口–>Executor顶层接口
- Executors静态工厂类,提供了很多的通用线程池的静态方法
线程池的作用
- 节省资源:复用线程,减少重复创建和销毁线程
- 提升响应速度:如果有可以复用的线程,不需要创建新线程,直接执行业务操作,提升速度
- 方便管理,提供了线程池的shutdown、shutdownnow、isTerminated、isShutDown等方法
- 削峰限流,不让一下子过多的线程把程序搞崩,比如RocketMQ里面的Consumer就会把获取到的消息放入到线程池里来慢慢消费public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
使用规则
优先级: 基本线程池 > BlockingQueue >最大线程池 >饱和策略
- 线程池判断基本线程池是否已满?没满,创建一个工作线程来执行任务,即使有空闲的已经创建的线程也不会复用。满了,则进入下个流程。
- 线程池判断工作队列是否已满?没满,则将新提交的任务存储在工作队列里。满了,则进入下个流程
- 线程池判断整个线程池是否已满?没满,则创建一个新的工作线程来执行任务,满了,则交给饱和策略来处理这个任务
runnableTaskQueue
该阻塞队列里面塞的不是普通的实体类,而是Runnable的实现类,和一般的生产消费模型不一样。线程池完成一个线程后,会尝试从队列里面getTask()。
- ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
- LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
- SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
- PriorityBlockingQueue:一个具有优先级得无限阻塞队列。
RejectedExecutionHandler(饱和策略)
- AbortPolicy,默认饱和策略,直接抛出异常
- CallerRunsPolicy:只用调用者所在线程来运行任务
- DiscardPolicy:不处理,丢弃掉,也不会日志什么的
- DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
- 自定义Police,需要实现RejectedExecutionHandler
线程池的使用 execute submit
execute 不会返回线程执行是否成功
threadsPool.execute(new Runnable() {});
submit 返回一个future对象可以阻塞获取返回值
Future future=threadsPool.submit(new Runnable() {});
Boolean result= future.get();
线程池的关闭
shutdown或shutdownNow方法来关闭线程池,但是它们的实现原理不同,
- shutdown的原理是只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。
- shutdownNow的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。shutdownNow会首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表。
Callable &Future &FutureTask
http://blog.csdn.net/javazejian/article/details/50896505
Callable接口
加强版的Runnable,有返回值,会抛出异常
public interface Runnable {public abstract void run();}public interface Callable<V> {V call() throws Exception;}可以作为参数,被线程池调用执行,Future
submit(Callable task);
Future接口
作为异步计算的顶层接口,Future对具体的Runnable或者Callable任务提供了三种操作:
- 执行任务的取消:cancel()
- 查询任务的状态: isDone()、isCancelled()
- 获取任务的执行结果(只有Callable任务才有,而且是阻塞):get()、V get(Long timeout , TimeUnit unit)
FutureTask类(同时实现了Runnable、Future)
- 既可以作为一个Runnable实现类在线程池中执行,甚至可以直接new Thread(futureTask).start();也可以作为一个future获取其他线程的执行结果和状态,
- done():异步计算完成后调用的回调函数
数据库连接池原理
- 解决的问题:频繁的建立、关闭连接,会极大的减低系统的性能,因为对于连接的使用成了系统性能的瓶颈。解决方案:连接复用。
- 大部分连接池可以设置最大连接数,最小连接数,最大闲置时间等参数,连接池根据这些参数,新建和关闭连接
使用案例
Executors 类里面实现了一些常用的工具方法,返回适合不同业务场景的线程池实现类。
但是要注意FixedThreadPool和SingleThreadPool都是定长的线程池,但是排队队列都是无限长的;CachedThreadPool和ScheduledThreadPool都是可以启动无数个线程来完成功能,都要注意不能OOM了。
FixedThreadPool,固定线程数量的线程池,配合一个无界的LinkedBlockingQueue,重复复用指定数量的coresize的线程
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());}SingleThreadPool,固定使用一个线程来工作,但是可以无限堆积,因为是单个线程,所以它可以保证认为是按顺序执行的,因为LinkedBlockingQueue是严格的FIFO的
|
CachedThreadPool,同步线程池,默认非公平的
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}ScheduledThreadPool,指定核心线程数的定时执行线程池,使用时通过 pool.schedule调用,指定延迟时间,依赖DelayedWorkQueue来实现
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {return new ScheduledThreadPoolExecutor(corePoolSize);}public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,new DelayedWorkQueue());}ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);for (int i = 0; i < 5; i++) {Future<Integer> result = pool.schedule(new Callable<Integer>() {@Overridepublic Integer call() throws Exception {int num = new Random().nextInt(100);//生成随机数System.out.println(Thread.currentThread().getName() + " : " + num);return num;}}, 3, TimeUnit.SECONDS);System.out.println(result.get());}pool.shutdown();
数据库的连接池管理
https://www.cnblogs.com/happySmily/p/5941813.html
常见的数据库连接池:dbcp、c3p0、druid | weblogic(收费的)
常见参数:
- initialSize=10: 程序一启动就自动创建 N个数据库长连接
- timeBetweenEvictionRunsMillis = “30000” ,程序启动后,每隔30scheck一下当前线程数是否合格:A)Idle线程是否超时,超时就回收掉 B)当前可用线程是否大于minIdle,小于就自动创建,如果设置为非正数,则不运行空闲连接回收器线程,默认是-1,不启动单独线程维护minIdle
- maxIdle:当可用线程>minIdle但是小于maxIdle的时候,空闲过期就会被回收
- maxActive:当可用线程超过maxIdle的,小于maxActive的时候,使用完就立刻回收
- maxWait=3000; 这个指的是最大等待时间,如果超过3秒还未分配到连接,就报错
|