JUC 线程池

概述

  1. 线程池的作用:节省资源、提升响应、削峰限流、管理线程

  2. ThreadPoolExecutor的核心参数:corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler

  3. Executors静态工厂类,提供常用的4种线程池:

  • FixedThreadPool,固定线程数量的线程池,配合一个无界的LinkedBlockingQueue
  • SingleThreadPool,固定使用一个线程来工作,但是可以无限堆积,因为是单个线程,所以它可以保证认为是按顺序执行的,因为LinkedBlockingQueue是严格的FIFO的
  • CachedThreadPool,同步线程池,默认非公平的,基于SynchronousQueue队列
  • ScheduledThreadPool,指定核心线程数的定时执行线程池,使用时通过 pool.schedule调用,指定延迟时间,依赖DelayedWorkQueue来实现
  1. 线程池使用的优先级: 基本线程池 > BlockingQueue >最大线程池 >饱和策略

  2. 线程池支持的函数

  • execute 不会返回线程执行是否成功
  • submit 返回一个future对象可以阻塞获取返回值
  • shutdown的原理是只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。

类的继承关系

http://cmsblogs.com/?p=2444

  1. Executor作为顶层接口,只提供一个execute()接口方法
  2. ThreadPoolExecutor继承AbstractExecutorService抽象类-->ExcutorService接口-->Executor顶层接口
  3. Executors静态工厂类,提供了很多的通用线程池的静态方法

线程池的作用

  1. 节省资源:复用线程,减少重复创建和销毁线程
  2. 提升响应速度:如果有可以复用的线程,不需要创建新线程,直接执行业务操作,提升速度
  3. 方便管理,提供了线程池的shutdown、shutdownnow、isTerminated、isShutDown等方法
  4. 削峰限流,不让一下子过多的线程把程序搞崩,比如RocketMQ里面的Consumer就会把获取到的消息放入到线程池里来慢慢消费,Dubbo也有一个默认100长度的LinkedBlockQueue
 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

使用规则

优先级: 基本线程池 > BlockingQueue >最大线程池 >饱和策略

  1. 线程池判断基本线程池是否已满?没满,创建一个工作线程来执行任务,即使有空闲的已经创建的线程也不会复用。满了,则进入下个流程。
  2. 线程池判断工作队列是否已满?没满,则将新提交的任务存储在工作队列里。满了,则进入下个流程
  3. 线程池判断整个线程池是否已满?没满,则创建一个新的工作线程来执行任务,满了,则交给饱和策略来处理这个任务

runnableTaskQueue

常用阻塞队列特性总结:通过加锁实现安全地读写(size、contains函数也是加锁读),通过condition或者AtomicInteger来协调生产和消费

  1. ArrayBlockQueue:1把锁,无法并发写,2个condition,通过await、signal来实现线程调度。FIFO队列的定长阻塞队列
  2. LinkedBlockQueue:2把锁,支持生产、消费并发执行,通过AtomicInteger和CAS来控制库存。可以指定大小,默认无界队列。
  3. SynchronousQueue:没有容量的队列,put、take成为一对儿才可以执行,否则阻塞,add和remove如果没有配对成功,直接报错。用于快速响应的业务场景
  4. PriorityBlockingQueue:有优先级的队列,插入元素实现Compare接口,内部维护了一个最小堆,一把锁,基于数组,自动扩容的无界队列

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<T> submit(Callable<T> 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了。

  1. FixedThreadPool,固定线程数量的线程池,配合一个无界的LinkedBlockingQueue,重复复用指定数量的coresize的线程
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
  1. SingleThreadPool,固定使用一个线程来工作,但是可以无限堆积,因为是单个线程,所以它可以保证认为是按顺序执行的,因为LinkedBlockingQueue是严格的FIFO的
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
  1. CachedThreadPool,同步线程池,默认非公平的
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
  1. 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>() {

                @Override
                public 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(收费的)
常见参数:

  1. initialSize=10: 程序一启动就自动创建 N个数据库长连接
  2. timeBetweenEvictionRunsMillis = "30000" ,程序启动后,每隔30scheck一下当前线程数是否合格:A)Idle线程是否超时,超时就回收掉 B)当前可用线程是否大于minIdle,小于就自动创建,如果设置为非正数,则不运行空闲连接回收器线程,默认是-1,不启动单独线程维护minIdle
  3. maxIdle:当可用线程>minIdle但是小于maxIdle的时候,空闲过期就会被回收
  4. maxActive:当可用线程超过maxIdle的,小于maxActive的时候,使用完就立刻回收
  5. maxWait=3000; 这个指的是最大等待时间,如果超过3秒还未分配到连接,就报错
maxWait="3000"    从池中取连接的最大等待时间,单位ms.  
initialSize="10"  初始化连接    
minIdle="10"   最小空闲连接  
maxIdle="60"   最大空闲连接
maxActive="80" 最大活动连接  

validationQuery = "SELECT 1"  验证使用的SQL语句  
testWhileIdle = "true"      指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除.  
testOnBorrow = "false"   借出连接时不要测试,否则很影响性能  
timeBetweenEvictionRunsMillis = "30000"  每30秒运行一次空闲连接回收器  
minEvictableIdleTimeMillis = "1800000"  池中的连接空闲30分钟后被回收  
numTestsPerEvictionRun="10" 在每次空闲连接回收器线程(如果有)运行时检查的连接数量  
      
removeAbandoned="true"  连接泄漏回收参数,当可用连接数少于3个时才执行  
removeAbandonedTimeout="180"  连接泄漏回收参数,180秒,泄露的连接可以被删除的超时值  

参考资料

大牛博客:宏观上分析 线程池
http://ifeve.com/java-threadpool/

CSDN系列专栏简易
http://blog.csdn.net/column/details/javathreadpool.html

死磕java并发系列有4篇线程池相关的
http://cmsblogs.com/?p=2122

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容

  • 一.Java中的ThreadPoolExecutor类 java.uitl.concurrent.ThreadPo...
    谁在烽烟彼岸阅读 645评论 0 0
  • (1) 其实我一直都不愿意透露自己的职业,因为我知道,一旦曝光了我的职业,很多人都会对我避而远之。 最关键的是刚认...
    兔兔纸阅读 945评论 18 10
  • 《哈利波特》是许多人共同的回忆,2017年9月1日对许多书迷来说是一个特别重要的日子,在这个日期发生了《哈利波特》...
    傻鸡包阅读 176评论 0 0
  • 我战胜了自己 “妈妈,我肚子好疼!” 我捂着肚子说。“那你快去上侧所。”妈妈说。当我走到走廊时,我便...
    欢乐洋阅读 175评论 0 1
  • 孤寂的狂野却延伸不了黑夜的狂躁;满目的葱绿却掩盖不了心灵的荒芜;偌大的郑州却承载不下思乡的孤独。 ...
    坚强的美美阅读 94评论 2 2