java多线程——线程池

admin2024-05-15  1

概述

线程池是管理java线程生命周期的工具

  • 降低资源消耗。通过池化技术能够重复利用已创建的线程,降低线程频繁创建和销毁造成的资源消耗
  • 提高线程的可管理性。无需程序员手动销毁线程,控制线程创建的数量,避免无限制的创建影响系统稳定性

线程池的运行机制

线程池的核心参数

  1. corePoolSize(必需):核心线程数量。默认情况下,核心线程会一直存活,但是当allowCoreThreadTimeout 设置为true时,核心线程也会超时回收
  2. maximumPoolSize(必需):最大线程数量,当线程池内的线程数量到达该数之后,后续的新任务将交由拒绝策略处理
  3. keepAliveTime(必需):线程闲置超时时长。如果闲置线程的时间超过该时长,非核心线程就会被回收,如果allowCoreThreadTimeout 设置为true时,核心线程也会超时回收
  4. unit(必需):keepAliveTime参数的时间单位
  5. workQueue(必需):线程等待队列即任务等待的队列
  6. threadFactory(可选):线程工厂,用于指定线程池创建新线程的方式
  7. handler(可选):拒绝策略,当到达最大线程数时需要执行的拒绝策略, 默认为AbortPolicy即丢弃任务并抛异常

提交任务的流程

java多线程——线程池,第1张

线程池实现类——ThreadPoolExecutor

构造方法

参数说明

ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue<Runnable> workQueue)

corePoolSize:核心线程池大小

maximumPoolSize:最大线程池大小

keepAliveTime:当线程数>核心线程数时,这是 多余线程在任务执行完成后最大的存活时间

unit:时间单位

workQueue:线程等待队列即任务等待的队列

ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue<Runnable> workQueue,

ThreadFactory threadFactory)

corePoolSize:核心线程池大小

maximumPoolSize:最大线程池大小

keepAliveTime:当线程数>核心线程数时,这是 多余线程在任务执行完成后最大的存活时间

unit:时间单位

workQueue:线程等待队列即任务等待的队列

threadFactory:线程创建工厂

ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue<Runnable> workQueue,

RejectedExecutionHandler handler)

corePoolSize:核心线程池大小

maximumPoolSize:最大线程池大小

keepAliveTime:当线程数>核心线程数时,这是 多余线程在任务执行完成后最大的存活时间

unit:时间单位

workQueue:线程等待队列即任务等待的队列

handler:拒绝执行某个线程的策略LinkedBlockingQueue

LinkedBlockingQueue<Runnable>

队列默认size=Integer.MAX_VALUE,即是一个无界队列

底层实现是链表;读和写使用的是两个不同的锁,所以是读写分离的队列。

阻塞操作:

take();//当队列中无元素时,阻塞等待

put();//当队满时阻塞等待

非阻塞操作:

poll();//当队列为null时,返回null

offer();//堆满时return false;

功能线程池

Executors已经为我们封装好了四种常见的功能线程池:

  • 定长线程池(FixedThreadPool)
  • 定时线程池(ScheduledThreadPool )
  • 可缓存线程池(CachedThreadPool)
  • 单线程化线程池(SingleThreadExecutor)

java多线程——线程池,第2张

从结构图中能看到四种线程池的真正实现类是ThreadPoolExecutor

线程池类型

1、newScheduledThreadPool 调度型线程池

  • 支持定时及周期性的执行任务,提交任务时支持设置任务的调度周期
  • 只限制了核心线程数,也就是最大线程数为Integer.MAX_VALUE,当添加任务的速度大于线程池处理任务的速度,可能会创建大量的线程,消耗资源,甚至导致OOM

1.1 构造

构造方法

参数

newScheduledThreadPool(int corePoolSize)

corePoolSize:corePoolSize

maximumPoolSize:Integer.MAX_VALUE

keepAliveTime:0

unit:NANOSECONDS

workQueue:DelayedWorkQueue

newScheduledThreadPool( int corePoolSize, ThreadFactory threadFactory)

1.2 任务队列-DelayedWorkQueue

  • 该队列的数据结构是优先级队列(依赖堆实现),当新来任务时将该task插入堆并根据优先级重新调整堆,保证优先级最高的task在fist index
  • 执行任务时,获取fist index中的task,并保证等待够delay时间后再执行(当fist task执行后也需要重新调整堆)

java多线程——线程池,第3张

2、newCachedThreadPool 缓存型线程池

  • 无核心线程数,无非核心线程数,执行完闲置60s回收,任务队列为不存储元素的阻塞队列
  • 适用于短期异步的小任务,或负载较轻的服务器
  • 创建的最大线程数为Integer.MAX_VALUE,当添加任务的速度大于线程池处理任务的速度,可能会创建大量的线程,消耗资源,甚至导致OOM

2.1 构造方法

构造方法

参数

ExecutorService newCachedThreadPool()

参数默认值

corePoolSize:0

maximumPoolSize:Integer.MAX_VALUE

keepAliveTime:60L

unit:TimeUnit.SECONDS

workQueue:SynchronousQueue<Runnable>

ExecutorService newCachedThreadPool(ThreadFactory threadFactory)

2.2 任务队列-SynchronousQueue

  • 队列是不存储元素缓存队列的,每次要进行offer操作时必须等待poll操作,否则不能继续添加元素
  • 当前线程A执行offer操作将task添加到queue中,若没有另一个线程来消费该task,那线程A会一直阻塞直到另一个线程来消费该task

java多线程——线程池,第4张

3、newFixedThreadPool

  • 创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程
  • 适用于限制当前线程数量的应用场景,适用于负载比较重的服务器
  • 创建了一个无界队列LinkedBlockingQueuesize,是一个最大值为Integer.MAX_VALUE的线程阻塞队列,当添加任务的速度大于线程池处理任务的速度,可能会在队列堆积大量的请求,消耗很大的内存,甚至导致OOM

3.1 构造方法

构造方法

参数

ExecutorService newFixedThreadPool(int nThreads)

corePoolSize:nThreads

maximumPoolSize:nThreads

keepAliveTime:0L

unit:TimeUnit.MILLISECONDS,

workQueue:LinkedBlockingQueue<Runnable>

threadFactory

ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)

3.2 任务队列-LinkedBlockingQueue<Runnable>

  • 队列默认size=Integer.MAX_VALUE即是一个无界队列,底层实现是链表,当添加任务的速度大于线程池处理任务的速度,可能会在队列堆积大量的请求,消耗很大的内存,甚至导致OOM
  • 读和写使用的是两个不同的锁,所以是读写分离的队列

4、SingleThreadExecutor

  • 创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务,如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它
  • 此线程池保证所有任务的执行顺序按照任务的提交顺序执行

4.1 构造方法

构造方法

参数

ExecutorService newSingleThreadExecutor()

corePoolSize:1

maximumPoolSize:1

keepAliveTime:0L

unit:TimeUnit.MILLISECONDS,

workQueue:LinkedBlockingQueue<Runnable>

threadFactory

ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)

4.2 任务队列-LinkedBlockingQueue<Runnable>

  • 队列默认size=Integer.MAX_VALUE即是一个无界队列,底层实现是链表,当添加任务的速度大于线程池处理任务的速度,可能会在队列堆积大量的请求,消耗很大的内存,甚至导致OOM
  • 读和写使用的是两个不同的锁,所以是读写分离的队列

线程池拒绝策略:

实现java.util.concurrent.RejectedExecutionHandler接口

1,CallerRunsPolicy:当想提交任务的线程池还存活,就由提交task的当前线程来执行

2,AbortPolicy:丢弃任务并抛出RejectedExecutionException异常,(默认拒绝策略)

3,DiscardPolicy:丢弃任务但是不抛异常

4,DiscardOldestPolicy:当想提交任务的线程池还存活,就将丢弃队列最前面的任务,然后重新提交被拒绝的任务

对比

类型

池内线程数量

任务队列

特点

应用场景

newScheduledThreadPool

核心线程:固定

非核心线程:无限制

DelayedWorkQueue

  1. 定时,周期性执行

执行定时/周期性任务

newCachedThreadPool

无限制

SynchronousQueue

  1. 无最大线程数限制
  2. 队列不存储元素,当没有空闲线程时会立即新建线程
  3. 当添加任务的速度大于线程池处理任务的速度,可能会创建大量的线程,消耗资源,甚至导致OOM

执行数量多,耗时少的线程任务

newFixedThreadPool

固定

LinkedBlockingQueue

  1. 每来一个任务就会新建线程,直到线程数到达指定的数量
  2. 核心线程数处于空闲状态也不会被回收,除非线程关闭
  3. 当所有线程都处于活跃状态,新的任务都会处于等待状态,直到有线程空闲出来
  4. 任务队列无限制

控制线程的最大并发数

newSingleThreadExecutor

1个

LinkedBlockingQueue

  1. 保证所有任务按照指定顺序在一个线程中方执行
  2. 不需要处理线程同步的问题

单线程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明原文出处。如若内容造成侵权/违法违规/事实不符,请联系SD编程学习网:675289112@qq.com进行投诉反馈,一经查实,立即删除!