侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

java线程池详解

Java  /  管理员 发布于 8年前   168

线程池概述

1. 线程池就是一个管理线程的池子,可以降低创建和销毁线程带来的资源消耗

因为线程其实也是一个对象,创建一个对象,需要经过类加载过程,销毁一个对象,需要走GC垃圾回收流程,都是需要资源开销的。

2. 提高响应速度,任务到达了相对于从线程池取线程,自己创建线程肯定慢很多

3. 重复利用,线程用完了再放回池子,达到了重复利用的效果

(推荐视频:java视频教程)

线程池执行

打个比喻

核心线程比作公司正式员工

非核心线程比作外包员工

阻塞队列比作需求池

提交任务比作提需求

1.jpg

正式执行

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime,TimeUnit unit,   BlockingQueue<Runnable> workQueue,   ThreadFactory threadFactory,   RejectedExecutionHandler handler)
corePoolSize     核心线程数maximumPoolSize  线程池最大线程数keepAliveTime    空闲线程存活时间TimeUnit         线程空闲存活时间单位workQueue        存放任务的阻塞队列threadFactory    线程工厂handler          饱和策略

● 提交一个任务,线程池里存活的核心线程数小于线程数corePoolSize时,线程池会创建一个核心线程去处理提交的任务。

● 如果线程池核心线程数已满,即线程数已经等于corePoolSize,一个新提交的任务,会被放进任务队列workQueue排队等待执行。

● 当线程池里面存活的线程数已经等于corePoolSize了,并且任务队列workQueue也满,判断线程数是否达到maximumPoolSize,即最大线程数是否已满,如果没到达,创建一个非核心线程执行提交的任务。

● 如果当前的线程数达到了maximumPoolSize,还有新的任务过来的话,直接采用拒绝策略处理。

几种饱和策略

AbortPolicy         抛出一个异常,默认的DiscardPolicy       直接丢弃任务DiscardOldestPolicy 丢弃队列里最老的任务,将当前这个任务继续提交给线程池CallerRunsPolicy    交给线程池调用所在的线程进行处理

线程池异常处理

由于在线程池调用线程处理任务过程中出现的异常可能会被线程池捕获,所以对于任务的执行可能是无感知的,因此我们需要考虑线程池异常情况。

方法一:

@Testpublic void test1() throws Exception {    ExecutorService executorService = Executors.newFixedThreadPool(5);    for (int i = 0; i < 5; i++) {        executorService.submit(new Runnable() {@Overridepublic void run() {    try {        System.out.println("name: " + Thread.currentThread().getName());        Object a = null;        System.out.println(a.hashCode());    } catch (Exception e) {        System.out.println(e);    }}        });    }}

方法二:

@Testpublic void test2() throws Exception {    ExecutorService executorService = Executors.newFixedThreadPool(5);    for (int i = 0; i < 20; i++) {        Future<?> future = executorService.submit(new Runnable() {@Overridepublic void run() {    System.out.println("name: " + Thread.currentThread().getName());    Object a = null;    System.out.println(a.hashCode());}        });        try {future.get();        } catch (Exception e) {System.out.println(e);        }    }}

线程池的工作队列

● ArrayBlockingQueue

● LinkedBlockingQueue

● SynchronousQueue

● DelayQueue

● PriorityBlockingQueue

==ArrayBlockingQueue==

● 初始化一定容量的数组

● 使用一个重入锁,默认使用非公平锁,入队和出队共用一个锁,互斥

● 是有界设计,如果容量满无法继续添加元素直至有元素被移除

● 使用时开辟一段连续的内存,如果初始化容量过大容易造成资源浪费,过小易添加失败

==LinkedBlockingQueue==

● 使用链表数据结构

● 非连续性内存空间

● 使用两个重入锁分别控制元素的入队和出队,用Condition进行线程间的唤醒和等待

● 有边界的,在默认构造方法中容量是Integer.MAX_VALUE

==SynchronousQueue==

● 内部容量是0

● 每次删除操作都要等待插入操作

● 每次插入操作都要等待删除操作

● 一个元素,一旦有了插入线程和移除线程,那么很快由插入线程移交给移除线程,这个容器相当于通道,本身不存储元素

● 在多任务队列,是最快的处理任务方式。

==PriorityBlockingQueue==

● 无边界设计,但容量实际是依靠系统资源影响

● 添加元素,如果超过1,则进入优先级排序

==DelayQueue==

● 无边界设计

● 添加(put)不阻塞,移除阻塞

● 元素都有一个过期时间

● 取元素只有过期的才会被取出

常用的线程池

● newFixedThreadPool (固定数目线程的线程池)

● newCachedThreadPool (可缓存线程的线程池)

● newSingleThreadExecutor (单线程的线程池)

● newScheduledThreadPool (定时及周期执行的线程池)

==newFixedThreadPool==

public static ExecutorService newFixedThreadPool(int nThreads) {    return new ThreadPoolExecutor(nThreads, nThreads,          0L, TimeUnit.MILLISECONDS,          new LinkedBlockingQueue<Runnable>());}

特点

1. 核心线程数和最大线程数大小一样

2. 没有所谓的非空闲时间,即keepAliveTime为0

3. 阻塞队列为无界队列LinkedBlockingQueue

工作机制:

2.png

● 提交任务

● 如果线程数少于核心线程,创建核心线程执行任务

● 如果线程数等于核心线程,把任务添加到LinkedBlockingQueue阻塞队列

● 如果线程执行完任务,去阻塞队列取任务,继续执行。

==newCachedThreadPool==

public static ExecutorService newCachedThreadPool() {    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,          60L, TimeUnit.SECONDS,          new SynchronousQueue<Runnable>());}

线程池特点

● 核心线程数为0

● 最大线程数为Integer.MAX_VALUE

● 阻塞队列是SynchronousQueue

● 非核心线程空闲存活时间为60秒

3.jpg

工作机制:

● 提交任务

● 因为没有核心线程,所以任务直接加到SynchronousQueue队列。

● 判断是否有空闲线程,如果有,就去取出任务执行。

● 如果没有空闲线程,就新建一个线程执行。

● 执行完任务的线程,还可以存活60秒,如果在这期间,接到任务,可以继续活下去;否则,被销毁。

使用场景

用于并发执行大量短期的小任务。

使用SynchronousQueue作为工作队列,工作队列本身并不限制待执行的任务的数量。但此时需要限定线程池的最大大小为一个合理的有限值,而不是Integer.MAX_VALUE,否则可能导致线程池中的工作者线程的数量一直增加到系统资源所无法承受为止。

如果应用程序确实需要比较大的工作队列容量,而又想避免无界工作队列可能导致的问题,不妨考虑SynchronousQueue。SynchronousQueue实现上并不使用缓存空间

==newSingleThreadExecutor==

线程池特点

● 核心线程数为1

● 最大线程数也为1

● 阻塞队列是LinkedBlockingQueue

● keepAliveTime为0

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {    return new FinalizableDelegatedExecutorService        (new ThreadPoolExecutor(1, 1,        0L, TimeUnit.MILLISECONDS,        new LinkedBlockingQueue<Runnable>(),        threadFactory));}

工作机制

4.png

● 提交任务

● 线程池是否有一条线程在,如果没有,新建线程执行任务

● 如果有,讲任务加到阻塞队列

● 当前的唯一线程,从队列取任务,执行完一个,再继续取,一个人(一条线程)夜以继日地干活。

使用场景

适用于串行执行任务的场景,一个任务一个任务的执行

==newScheduledThreadPool==

线程池特点

public ScheduledThreadPoolExecutor(int corePoolSize) {    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());}

● 最大线程数为Integer.MAX_VALUE

● 阻塞队列是DelayedWorkQueue

● keepAliveTime为0

● scheduleAtFixedRate() :按某种速率周期执行

● scheduleWithFixedDelay():在某个延迟后执行

工作机制

● 添加一个任务

● 线程池中的线程从 DelayQueue 中取任务

● 线程从 DelayQueue 中获取 time 大于等于当前时间的task

● 执行完后修改这个 task 的 time 为下次被执行的时间

● 这个 task 放回DelayQueue队列中

scheduleWithFixedDelay

● 无论任务执行时间长短,都是当第一个任务执行完成之后,延迟指定时间再开始执行第二个任务

scheduleAtFixedRate

● 在任务执行时间小于间隔时间的情况下,程序以起始时间为准则,每隔指定时间执行一次,不受任务执行时间影响

● 当执行任务时间大于间隔时间,此方法不会重新开启一个新的任务进行执行,而是等待原有任务执行完成,马上开启下一个任务进行执行。此时,执行间隔时间已经被打乱

本文来自,java教程栏目,欢迎学习!

以上就是java线程池详解的详细内容,更多请关注其它相关文章!


  • 上一条:
    Docker下搭建一个JAVA Tomcat运行环境的方法
    下一条:
    命令行运行java的class文件提示找不到或无法加载主类
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在java中实现的脱敏工具类代码示例分享(0个评论)
    • zookeeper安装流程步骤(0个评论)
    • 在java中你背的“八股文”可能已经过时了(2个评论)
    • 在php8.0+版本中使用属性来增加值代码示例(3个评论)
    • java 正则表达式基础,实例学习资料收集大全 原创(0个评论)
    • 近期文章
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-11
    • 2018-03
    • 2020-03
    • 2023-05
    • 2023-11
    • 2024-01
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客