java线程池

线程池是一种池技术,就像连接池一样。线程池本身也是一个对象,这个对象可以管理自己池子中的众多线程,以使他们被高效率的反复利用

  • 为何要使用线程池

  1. 降低资源消耗,防止资源不足。
    线程也是对象,频繁的创建线程对象,就会频繁的去触发内存分配和内存占用,大量消耗性能,内存占用过多可能会导致内存溢出。
    频繁的创建和销毁线程对象即频繁的消耗cpu,影响服务器性能。
    因为有大量的对象产生,就会有大量的GC回收,大量的gc可能会导致gc抖动,卡顿等现象,也会影响服务器性能。
  2. 提高响应速度
    当需要被线程执行的任务到达时,因线程池中已有创建好的线程,所以可以直接用创建好的线程去执行任务,而无需先创建一个线程,然后在执行任务,节省了程序运行时间。
  3. 提高线程的可管理性
    可以控制线程的数量和并法数,也可以防止无限制的创建线程数量导致性能和内存溢出等问题。
  4. 提供更强大的功能,延时定时线程池
    比如,我们需要一个任务每一秒钟执行一次。
  • java线程池种类

  1. newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

  2. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

  3. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

  4. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行

不过在工作中,如果你使用Executors的方式去创建线程,可能会失业的,原因直接贴上阿里开发手册的说明。

image.png

  • 正确创建线程池的姿势

image.png

  • 线程池创建线程执行和拒绝策略如下:

当一个任务通过 execute(Runnable) 方法欲添加到线程池时,线程池采用的策略如下(即添加任务的策略):

如果此时线程池中的数量小于 corePoolSize ,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。

如果此时线程池中的数量等于 corePoolSize ,但是缓冲队列 workQueue 未满,那么任务被放入缓冲队列。

如果此时线程池中的数量大于 corePoolSize ,缓冲队列 workQueue 满,并且线程池中的数量小于maximumPoolSize ,建新的线程来处理被添加的任务。

如果此时线程池中的数量大于 corePoolSize ,缓冲队列 workQueue 满,并且线程池中的数量等于maximumPoolSize ,那么通过 handler 所指定的策略来处理此任务。

  • 使用有界队列时,如果任务队列满了则执行拒绝策略(第二个用的多些)

  1. ThreadPoolExecutor.AbortPolicy 丢弃任务,并抛出 RejectedExecutionException 异常。备注:此种方法必须要捕获线程池添加任务代码,不然添加异常就不会走shutdown关闭线程池代码,会导致线程池一直处于未关闭状态。

  2. ThreadPoolExecutor.CallerRunsPolicy:该任务被线程池拒绝,由调用 execute方法的线程执行该任务。备注:其实就是主线程去执行被线程池拒绝的任务。

  3. ThreadPoolExecutor.DiscardOldestPolicy : 抛弃队列最前面的任务,然后重新尝试执行任务。备注:就是移除队列中第一个添加进去的任务,把后来的任务加在队列最后。

  4. ThreadPoolExecutor.DiscardPolicy,丢弃任务,不过也不抛出异常。备注:直接丢弃队列满了之后添加进来的任务。

  • 如何优雅的关闭线程池:

线程池有2种关闭方法:

  1. shutdown():线程池拒接收新提交的任务,同时等待线程池里的任务执行完毕后关闭线程池。
  2. shutdownNow() :线程池拒接收新提交的任务,同时立马关闭线程池,线程池里的任务不再执行,并能获取未被执行的任务。

关闭线程池的时候,线程池中的线程有4种状态:

  1. 处于空闲状态 — shutdown: 正常退出,shutdownNow: 正常退出
  2. 处于正在处理任务的状态 — shutdown: 任务处理完成正常退出,shutdownNow: 任务处理完成正常退出
  3. 处于正在从任务队列读取任务的状态— shutdown: 继续读取并任务执行,直到所有任务全部执行完毕退出,shutdownNow: 停止读取并退出
  4. 处于阻塞状态— shutdown: 线程继续阻塞,并等待阻塞结束继续执行任务,shutdownNow: 抛出InterruptedException异常

所以,我们该如何正确关闭线程池

  • 当调用shutdownNow()方法时,如果确定可能会有阻塞的任务存在,一定要捕获异常进行处理
  • 当调用shutdown()方法时,一定要确保任务里不会有永久阻塞等待的线程,否则线程池就关闭不了,不行的话可以等待一段时间后调用shutdownNow()方法

如果要在线程池任务执行完关闭之后才能执行其他的主逻辑,那么我们就必须要等到线程池任务全部执行结束,需要注意的是,不管是shutdown还是shutdownNow方法,其实都是发起线程关闭,但是线程池此时并不一定完全关闭了,因为可能有线程还在执行任务或者队列里还有任务等待执行,所以我们需要通过pool.awaitTermination(2, TimeUnit.SECONDS)方法去判断线程池是否真的完全关闭了

  • pool.awaitTermination(2, TimeUnit.SECONDS)
    第一个参数为时间,第二个参数为单位
    这是一个阻塞方法,返回true和false,线程池关闭为true,未关闭为false
    在指定时间内,如果线程池关闭了,此方法结束阻塞,返回true,继续执行之后的代码
    在指定时间内,如果线程池未关闭,会一直阻塞,直到指定时间到了返回false。
    注意:此方法为阻塞方法,如果任务没有结束,子线程会阻塞于此,主线程也会被阻塞等待,当需要线程池的任务全部结束才能执行主线程时,可以用此方法,输入一个时间比较长的参数
    所以,一般shutdown()或者shutdownNow()方法要配合awaitTermination()方法一起使用。

其实关键就在于是否有阻塞的任务,下面是一种参考处理方法

threadPool.shutdown(); // Disable new tasks from being submitted
        // 设定最大重试次数
        try {
            // 等待 60 s
            if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
                // 调用 shutdownNow 取消正在执行的任务
                threadPool.shutdownNow();
                // 再次等待 60 s,如果还未结束,可以再次尝试,或者直接放弃
                if (!threadPool.awaitTermination(60, TimeUnit.SECONDS))
                    System.err.println("线程池任务未正常执行结束");
            }
        } catch (InterruptedException ie) {
            // 重新调用 shutdownNow
            threadPool.shutdownNow();
        }

线程关闭的参考资料:
https://www.cnblogs.com/qingquanzi/p/9018627.html
https://cloud.tencent.com/developer/article/1523115
https://blog.csdn.net/chun_hua_xue_yue/article/details/96475075

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

推荐阅读更多精彩内容