线程池的应用

一、写在前面

听到线程池、线程,不禁都为为之端正姿势

二、部分基础

1、两种线程模型

  1. 用户级线程(ULT):用户程序实现,不依赖操作系统核心,应用提供创建、同步、调度和管理新城的函数来控制用户线程。不需要用户态/内核态之间的切换,速度快,内核对ULT无感知。
  2. 内核线程(KLT):系统内核管理线程,内核保存线程的状态和上下文,线程阻塞不会引起进程阻塞,在多处理器系统上,多线程在处理器上并行运行,线程创建、调度和管理由内核完成,效率要比ULT慢,比进程操作快。

直观感受下内核线程:如果创建的是内核线程,那么这个数字会增多。
在这里插入图片描述
Java线程创建是依赖于系统内核,通过JVM调用系统库创建内核线程,内核线程与Java-Thread是1:1关系

2、使用线程池的原因

线程创建太麻烦,Java是依赖于内核线程,创建和销毁需要保存线程的状态和切换上下文,是一个小消耗资源的操作。为了避免资源消耗过度需要设法重用线程的执行多任务。线程池就是一个线程缓存,负责对线程进行统一分配、调优与监控。

3、什么时候使用线程池

单个任务处理时间比较快
需要处理的任务数量比较大

4、线程池的优势

重用存在的线程,减少创建线程、消亡的开销,提升性能
提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行
提高线程管理型,可以统一分配、调优和监控

5、包关系

所有的线程都是继承自Executor,Executor是一个超类

ThreadPoolExecutor:比较常用
FrokJoinPool:幂级类
ScheduledTreadPoolExecutor:延时类
ThreadPoolTaskExecutor
ThreadPoolTaskScheduler

6、线程池的几个参数

corePoolSize:核心线程池大小
maximumPoolSize:最多能创建多少个线程
keepAliveTime:如果没有工作,最长可以空闲多长时间
unit:时间单位
workQueue:都在工作状态,都阻塞,提供的阻塞队列,处理不过来,就放在阻塞队列。在任意时刻,不管并发多高,永远只有一个线程能够进队或出队,线程是安全的。
handler:阻塞队列已满,且线程数达到最大,所采用的饱和策略

7、线程池的工作原理

用工作原理说线程池的几个参数
先上图镇楼
在这里插入图片描述

  1. 来了通过execute提交过来任务先进入corePool
  2. 当corePoll满了进入阻塞队列
  3. 当队列满了使用MaximumPoolSize的数量开启线程
  4. 当线程超过了MaximumPoolSize的数量,走饱和策略(拒绝策略)

8、线程池常用的阻塞队列及选择

1.无界队列

队列大小无限制,常用的无界队列LinkdeBlackQueue,使用该队列作为阻塞队列时要尤其小心,当任务耗时较长时,可能会导致大量的新任务在队列中堆积最终导致OOM 1 。Executors.newFixedThreadPool()采用的就是LinkedBlockingQueue

2.有界队列

常用的有两类

  1. 遵循FIFO2 原则的队列,例如:ArrayBlockingQueue
  2. 优先级队列,例如:PriorityBlockingQueue

使用有界队列时队列大小和线程池大小相互配合,线程池较小有界队列较大时可减少内存消耗,降低CPU的使用率和上下文切换,但有可能会限制系统吞吐量。

3.同步移交队列

如果不希望任务在队列中等待而是希望将任务直接移交工作线程,可以使用SynchronousQueue作为等待队列SyncronousQueue不是一个真正的队列,而是一种线程之间移交的机制。只有在使用无界线程或有饱和策略时才建议使用该队列

9、饱和策略的选择

Java提供四种,最后一个是Tomcat的
在这里插入图片描述

1.默认AbortPolicy

终止策略:使用该策略在饱和时会抛出RejectedExectionExcetion(继承自RuntimeException),调用者可以捕捉该异常自行处理

2.DiscardPoicy

抛弃策略:不做任何处理,相当于直接抛弃任务,看下源码直接就是{}什么都没有做

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }

3.DiscardOldestPolicy

抛弃旧任务策略:先将阻塞队列中的头元素出队抛弃,再尝试提交任务。如果此时阻塞队列使用PriorityBlockQueue优先队列,将会导致优先级最高的任务被抛弃,因此不建议将该种策略配合优先级队列使用。
看一下源码

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }

4.CallerRunsPolicy

调用者运行:既不抛弃任务也不抛弃异常,直接运行任务的run方法,换言之将任务回退给调用者直接运行。使用该策略时线程池饱和后将由调用线程池的主线程自己来执行任务,因此在执行任务的这段时间里主线程无法再提价新任务,从而使线程池中工作线程有时间将正在处理的任务处理完成。
看下源码:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }

10、声明周期

再上图镇楼
在这里插入图片描述

三、应用

1、创建线程池

本实例是开启5个线程,队列大小设置1000,饱和策略是调用者运行;

ExecutorService pool = new ThreadPoolExecutor(5, 5, 60L, TimeUnit.SECONDS, 
								new LinkedBlockingQueue<>(1000),
								new ThreadPoolExecutor.CallerRunsPolicy());

2、使用

for(TempStoreInfo item : monoList){
    Runnable runnable = () -> {
        。。。
    };
    pool.execute(runnable);
}

3、停止

常用关闭线程

try {
    pool.shutdown();
    if (!pool.awaitTermination( 1 * 60 * 1000, TimeUnit.MILLISECONDS)) { //设置超时
        // 超时的时候向线程池中所有的线程发出中断(interrupted)。
        log.error("超时了。。。。中断所有子进程,停止所有任务");
        pool.shutdownNow();
    }
} catch (InterruptedException e) {
    // awaitTermination方法被中断的时候也中止线程池中全部的线程的执行。
    pool.shutdownNow();
}

躬身自省,淳朴而谦逊否——文文的博客
前辈见之,如有问题,麻烦留言斧正。


  1. OOM:Out Of Memory,在学习JVM虚拟机时有过这个概念.
    元空间溢出是Out of Memory Error Metaspace,java8后方法区由本地内存管理内地化一份元空间。用作存储类信息(类名、父类、成员变量、方法等)、运行时常量池、类加载器、静态变量。
    堆内存溢出是Out of Memory heap,存储对象、数组。
    他们都是OOM ↩︎

  2. FIFO:先进先出队列 ↩︎

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java线程池在实际应用中非常常见,以下是一些常见的线程池应用实例: 1. Web服务器:在Web服务器中,可以使用线程池来处理客户端请求。每当有一个请求到达时,可以将其封装成一个任务提交给线程池线程池会自动分配线程来处理请求,从而提高服务器的并发性能。 2. 文件下载器:在文件下载器中,可以使用线程池来同时下载多个文件。每个文件可以作为一个独立的任务提交给线程池线程池会自动创建并管理多个线程来并发下载文件,加快下载速度。 3. 数据库连接池:在使用数据库连接时,可以使用线程池来管理数据库连接。通过将每个数据库操作封装成一个任务提交给线程池线程池可以管理连接的创建和释放,避免频繁地创建和关闭数据库连接,提高数据库操作的效率。 4. 定时任务调度:在定时任务调度中,可以使用线程池来执行定时任务。可以将每个定时任务封装成一个任务提交给线程池线程池会根据设定的时间间隔自动执行任务,实现定时任务的调度功能。 5. 并行计算:在需要进行大量计算的场景下,可以使用线程池来进行并行计算。将计算任务分解成多个子任务,每个子任务作为一个独立的任务提交给线程池线程池会自动创建并管理多个线程来并行执行计算任务,提高计算速度。 这些只是一些常见的应用实例,实际上线程池Java开发中的应用非常广泛,可以根据实际需求灵活地运用线程池来提高程序的性能和并发处理能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

园长的牧歌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值