线程和线程池简单学习笔记

本文深入探讨了线程和线程池的概念,包括用户级线程与内核级线程的区别。线程池通过减少线程创建和销毁的开销,提高系统资源利用率。介绍了线程池的执行原理,如核心线程、阻塞队列和拒绝策略。同时,讨论了线程池的状态转换,并提到了几种常见的线程池类型及其适用场景。强调自定义线程池时需考虑的核心线程数、最大线程数、空闲时间等参数设置。
摘要由CSDN通过智能技术生成

线程和线程池

个人博客:kana.chat:90

线程:线程即资源调度的最小单位,在linux中即为轻量级进程LWP(light weight process)

线程可分大致两种:
用户级线程(ULT)由用户程序实现,不依赖操作系统核心,应用提供创建、同步、调度、管理线程的函数控制线程,不需要用户态/内核态切换,线程阻塞进程(包括它的所有线程)

内核级线程(KLT) 由系统内核管理线程,内核保存线程状态和上下文信息,线程阻塞不会引起进程阻塞,多线程在多处理器的系统上并行运行,线程的创建、管理、调度由内核管控,效率慢与用户级线程,快于进程。

图片.png

对于绝大多数java虚拟机,使用的是内核级线程。

图片.png
   针对于大量的线程的创建和切换,使用线程池更利于线程的维护。线程池有利于线程的重用而执行多个任务,其本身是线程的缓存,负责对线程进行统一分配、调优和监控。
在单个任务处理时间短且有多个任务的时候,使用线程池效率较高。而阿里巴巴开发手册中也写到,使用线程池前必须自定义线程池。

线程池顶层接口Executor:
图片.png

线程池执行原理:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YoXjluGW-1602049148204)(https://kana-bucket.oss-cn-beijing.aliyuncs.com/%E5%9B%BE%E7%89%87_1598251616469.png)]
   由上图也可看出,线程池使用了阻塞队列(有界||无界),任意时刻只有一个线程能够进出队列,而且当队列满或者空的时候,也会被阻塞(等待出队/入队)。
   每次线程池只会执行固定的任务(每个任务必须实现runnable接口)。
线程池初始化的时候内部没有线程,当任务提交会去创建核心线程,当核心线程已满会将新提交的任务放入阻塞队列(BlockingQueue,这一块不同线程池使用不同的阻塞队列,可以是有界或者无界队列)中。
   若提交新任务时阻塞队列已满且当前核心线程仍然忙碌时,会创建临时线程去执行新提交的任务,临时线程若空闲时间超过指定的时间(即一段时间内现有任务不足以让临时线程去工作),临时线程就会被取消(本质上是随机取消)。如果临时线程数量也已满,线程池内部会提供自带的拒绝策略(使用者也可以直接扩充)。

   如上图:AbortPolicy(默认使用的拒绝策略)抛出异常,DiscardPolicy丢弃阻塞队列最旧的任务,DiscardPolicy什么也不做,CallerRunsPolicy: 如果线程池没有SHUTDOWN的话,直接执行任务。
   自定义线程池时需要设置核心线程数(corePoolSize)、最大线程数(maximumPoolSize)、临时线程最大空闲时间(keepAliveTime)、阻塞队列、拒绝策略等。线程池不保证任务的顺序,其一因为临时线程的存在,其二因为线程的并发性导致cpu的任务调度不一定是先来先服务。

线程池的五种状态:

图片.png
Running:能接受新任务,并且处理已经添加的任务
Shutdown:不接受新任务但可以处理已经添加的任务
Stop:不接受新任务也不处理已经添加的任务,并且中断正在处理的任务
Tidying:所有的任务已经终止,ctl记录的“任务数量”为0
注:ctl负责记录线程池的运行状态与活动线程数量
Terminated:线程池彻底终止,转入terminated状态

   每次线程工作时都会将任务包装成一个Worker(一个线程池的内部类,继承了AQS并且实现了runnable接口),调用addWorker方法先判断线程的状态,之后创建一个Worker。Worker中会将每个任务放入firstTask,并且使用getThreadFactory.newThread(this)创建新的核心线程,最终Worker会执行task的run方法。

20200808071554_qdjzk

线程池如何保证状态的切换

图片.png

图片.png

java中使用高3位记录线程生命状态,低29为记录当前工作线程数(上图位运算本质是对应的数的二进制左移29位)。

总结:
  1. 线程池倾向于使用核心线程来处理任务,从任务的添加策略可以看出,先考虑创建核心线程处理,再考虑放到阻塞队列,再考虑创建非核心线程处理。以上都不行,则使用任务拒绝策略

  2. 通过向阻塞队列取任务的不同操作,能确保线程的存活,take保证核心线程不死,poll保证非核心线程存活等待一定时间

  3. 线程池不区分核心线程和非核心线程,线程池是期望达到corePoolSize的并发状态,并允许在不得已情况下超载,达到corePoolSize ~ maximumPoolSize 的并发状态

  4. 线程池状态和线程数量用ctl表示,高三位为数量,低29位为当前线程池数量

  5. 线程池对状态的检测非常苛刻,几乎在所有稍微耗时或影响下一步操作正确性的代码前都校验ctl

常见的线程池选择
CachedThreadPool:适合异步任务多,但周期短的场景
FixedThreadPool:适合有一定异步任务,周期较长的场景,能达到有效的并发状态
SingleThreadExecutor:适合任务串行的场景
ScheduledThreadPool:适合周期性执行任务的场景

最后再放一张网图来清晰一下步骤:
图片.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值