JavaSE学习笔记---线程池

线程池

简介

什么是线程池

线程池(ThreadPool)是一种基于池化思想管理和使用线程的机制。它是将多个线程预先存储在一个“池子”内,当有任务出现时可以避免重新创建和销毁线程所带来性能开销,只需要从“池子”内取出相应的线程执行对应的任务即可。常见的运用池化思想的有:内存池、数据库连接池。使用线程池的优点如下:

  • 提高线程的利用率
  • 提高程序的相应速度
  • 便于统一管理线程对象

线程池的创建方式

Java线程池一共有7种,按创建类分为两种:

  • 通过java.util.concurrent的并发包下的Executors类创建
    • Executors.newFixedThreadPool创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。传奇老师交给我们的创建方式。
    • Executors.newCachedThreadPool创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程
    • Executors.newSingleThreadExecutor创建单个线程数的线程池,它可以保证先进先出的执行顺序
    • Executors.newScheduledThreadPool创建一个可以执行延迟任务的线程池
    • Executors.newSingleThreadScheduledExecutor创建一个单线程的可以执行延迟任务的线程池
    • Executors.newWorkStealingPool创建一个抢占式执行的线程池(任务执行顺序不确定)JDK 1.8 中添加
  • 通过ThreadPoolExecutor类创建最原始的创建线程池的方式,它包含了 7 个参数可供设置。
    一般使用Java提供了创建线程池的接口Executor(),推荐用子类ThreadPoolExecutor来创建线程池。这在阿里巴巴《Java开发手册》中有说明:

【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors 返回的线程池对象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool:允许的请求队列长度Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM(Out Of Memory Error 内存溢出异常)
2)CachedThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

通过ThreadPoolExecutor创建

该类参数最多的构造方法如下:

public ThreadPoolExecutor(int corePoolSize,
                         int maximumPoolSize,
                         long keepAliveTime,
                         TimeUnit unit,
                         BlockingQueue<Runnable> workQueue,
                         ThreadFactory threadFactory,
                         RejectedExecutionHandler handler)

参数:
corePoolSize - 池中所保存的线程数,包括空闲线程。
maximumPoolSize - 池中允许的最大线程数。
keepAliveTime - 在线程池中,核心线程即使在无任务的情况下也不能被清除,其余的线程都是有存活时间的,这里就是非核心线程可以保留的最长的空闲时间,非核心线程空闲时间达到的阈值会被回收。
unit - keepAliveTime 参数的时间单位。
workQueue - 执行前用于保持任务的队列,即阻塞(等待)队列。一共有ArrayBlockingQueue(有界阻塞队列)、LinkedBlockingQueue(无界阻塞队列)、SynchronousQueueSynchronousQueue实现原理等7种阻塞队列。大家都用过ArrayList和LinkedList,也经常在面试中问到他们之间的区别。ArrayList和ArrayBlockingQueue一样,内部基于数组来存放元素,而LinkedBlockingQueue则和LinkedList一样,内部基于链表来存放元素。LinkedBlockingQueue不同于ArrayBlockingQueue,它如果不指定容量,默认为Integer.MAX_VALUE,也就是无界队列。所以为了避免队列过大造成机器负载或者内存爆满的情况出现,我们在使用的时候建议手动传一个队列的大小。两种队列的数据结构详情:点击查看
阻塞队列的作用:一个作用时当一般队列中的任务满了后,阻塞队列可以保留之后的任务;另一个作用是,当队列没有任务时阻塞获取任务的线程、使其进入wait状态,释放CPU资源。阻塞队列自带阻塞和唤醒功能,不需要额外的处理,无任务时线程池利用阻塞队列的take方法挂起,从而保证核心线程的存货,不至于一直占用CPU资源。
threadFactory - 执行程序创建新线程时使用的工厂,主要用来创建线程,默认为正常优先级、非守护线程。
handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。

处理程序共有以下四种:

AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满
DisCardPolicy:不执行新任务,也不抛出异常
DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行
CallerRunsPolicy:直接调用execute来执行当前任务

常用方法的相关信息

execute

public void execute(Runnable command)
在将来某个时间执行给定任务。可以在新线程中或者在现有池线程中执行该任务。 如果无法将任务提交执行,或者因为此执行程序已关闭,或者因为已达到其容量,则该任务由当前 RejectedExecutionHandler 处理。
参数:
command - 要执行的任务。
抛出:
RejectedExecutionException - 如果无法接收要执行的任务,则由 RejectedExecutionHandler 决定是否抛出 RejectedExecutionException
NullPointerException - 如果命令为 null

shutdown

public void shutdown()
按过去执行已提交任务的顺序发起一个有序的关闭,但是不接受新任务。如果已经关闭,则调用没有其他作用。
抛出:
SecurityException - 如果安全管理器存在并且关闭此 ExecutorService 可能操作某些不允许调用者修改的线程(因为它没有 RuntimePermission("modifyThread")),或者安全管理器的 checkAccess 方法拒绝访问。

shutdownNow

public List shutdownNow()
尝试停止所有的活动执行任务、暂停等待任务的处理,并返回等待执行的任务列表。在从此方法返回的任务队列中排空(移除)这些任务。
并不保证能够停止正在处理的活动执行任务,但是会尽力尝试。 此实现通过 Thread.interrupt() 取消任务,所以无法响应中断的任何任务可能永远无法终止。
返回:
从未开始执行的任务的列表。
抛出:
SecurityException - 如果安全管理器存在并且关闭此 ExecutorService 可能操作某些不允许调用者修改的线程(因为它没有 RuntimePermission("modifyThread")),或者安全管理器的 checkAccess 方法拒绝访问。

执行流程

线程池关键节点的执行流程如下:

当线程数小于核心线程数时,创建线程。
当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
当线程数大于等于核心线程数,且任务队列已满:若线程数小于最大线程数,创建线程;若线程数大于等于最大线程数,抛出异常,拒绝任务。
线程池的执行流程如下图所示:
在这里插入图片描述

示例代码:

import java.util.concurrent.*;  
public class ThreadPoolTest {
    public static void main(String[] args) {
        ExecutorService executorService = new ThreadPoolExecutor(3,5,1L, TimeUnit.SECONDS,new ArrayBlockingQueue(4),Executors.defaultThreadFactory());//实例化对象
        for(int i=0;i<7;i++){
            executorService.execute(()->{
                System.out.println(Thread.currentThread().getName()+" "+"--->办理业务");
            });//执行任务
        }
        executorService.shutdown();
    }
}
posted @   效马生  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示