Java线程池

article/2025/9/10 3:43:05

   

目录

一、什么是线程池

二、线程池有哪些好处?

     ①降低资源的消耗

      ②提高响应速度

      ③提高线程的可管理能力

 三、线程池如何使用

       ①创建线程池​编辑

       工厂模式:

      工厂模式代码实现:

②往线程池当中添加任务

 四、Java当中有哪些线程池  ​编辑

    ①Executors.newFixedThreadPool

     ②Executors.newCacheThreadPool

     ③Executors.newScheduledThreadPool

     ④Executors.newSingleThreadExecutor

五、线程池的工作过程

      ①判断线程池当中运行线程的数量是否到达核心线程数量corePoolSize

       ②判断工作队列(存放任务的阻塞队列)是否已经满了

       ③再次创建新的线程

  经典面试问题:已知CPU的核心数为N,那么需要的线程池的线程数量是多少呢?

        (1)对于CPU密集型的任务:

       (2)对于IO密集型的任务

六、线程池ThreadPoolExecutor有哪些参数

    ①核心线程数量(corePoolSize)

    ②任务队列(runnableTaskQueue)

为什么线程池当中需要使用阻塞队列 

    ③最大线程数量(maximumPoolSize)

    ④keepAliveTime

     ⑤unit

     ⑥ThreadFactory

     ⑦RejectedExceptionHandler(饱和策略,也称为拒绝策略)

       (1)直接抛出异常

       (2)只用调用者所在线程来执行任务

       (3)丢弃队列当中的最近一个任务

       (4)不处理最新提交的任务 

七、设计一个简单的线程池       

八、为什么阿里巴巴开发规范禁用Executors

从构造方法分析

      从阻塞队列的长度分析


一、什么是线程池

       线程池,就是一个存放线程的池子。

       它的特点是,在使用线程之前,就一次性把多个线程创建好,放到"池”当中。

       后面需要执行任务的时候,直接从"线程池"当中通过线程执行。

       当线程执行完毕之后,也不是直接销毁线程,而是把线程重新放回到"池子“当中,等需要再次执行任务的时候,就再次从池子当中获取线程,执行任务。

      


二、线程池有哪些好处?

     ①降低资源的消耗

       通过重复利用已经创建的线程降低线程创建,销毁造成的开销。

       我们知道,传统的操作当中,通过调用thread.start()来创建线程,这一个步骤,实际上是调用了操作系统提供的api,来创建PCB,这一个过程涉及操作系统内部的系统调用。

       当线程执行完自己的任务之后,重新销毁线程。这个销毁的过程,也是交给操作系统完成的。

      而如果使用了线程池,可以有效减少不断创建、销毁线程带来的损耗。

      因为线程池创建之后,每次执行任务,都是从线程池当中获取线程,来执行任务,不用每次都通过调用api来获取。


      ②提高响应速度

         当任务需要执行的时候,任务可以不需要等待到线程创建,就可以立即执行任务。


      ③提高线程的可管理能力

         线程属于稀缺资源,如果无限制地创建线程,不仅仅会消耗系统资源,还会降低系统的稳定性。       

         如何理解所谓的"稳定性"呢?

         相比于把线程的获取交给操作系统内核来完成,通过线程池直接获取线程,可以提高程序的"可控性"。

         因为,如果直接创建线程,程序执行的时候,无法预知操作系统的内核当中究竟背负了多少的任务。操作系统内核也许背负了成千上万的任务......那轮到我需要执行的"创建线程"这个任务的时候,究竟响应速度怎样,能否响应都是问题......

         这样,也就无法预知通过操作系统获取线程的真实情况,也就提高了管理线程的难度。

        如果使用线程池来管理线程,那么如何从线程池当中获取线程,使用线程,这个过程都是可以控制,可以预料的。


 三、线程池如何使用

       ①创建线程池

        此处,10指定的是线程池当中的核心最大线程数量,后面会提及。

        线程池创建线程的方式,是通过"工厂模式"来实现的,是通过一个工厂来实现的。

        这里创建线程对象,并没有直接new N个线程对象,而是通过一个特定的工厂(ThreadFactory)来生产线程,后面会介绍到。


       工厂模式:

       可以简单地理解为:使用普通方法,代替构造方法来创建对象。

       如果想要获取对象,只能通过这个"普通方法"来获取对象。

        并且,可以通过这个"工厂"来获取多个对象。


      工厂模式代码实现:

        场景,如果我想构造一个点,根据数学的常识,可以使用极坐标的方式构造,也可以使用半径来构造,这两种构造方式,如下图所示:   


       可以看到,此时发生了编译报错。原因:构造方法重载的方式不正确,此时两个构造方法的参数列表完全相同。

       那如果我仍然想使得传入的参数类型一样,但是产生出不同含义的对象呢?那就需要使用到”工厂模式"。其中一个工厂,负责生产x,y轴构造的对象。另外一个负责生产r,a构造的对象。

     


②往线程池当中添加任务

         在①当中,已经创建好10个线程的任务了,那么,线程的任务,如果执行呢?

         此时,就需要使用submit()方法来把任务放到线程池当中,如下代码:

        

      上图代码的含义是,往一个线程池当中初始化10个线程,然后再往这个线程池当中提交1000个任务,由10个线程来"平均"分配一下这1000个任务。

       但是,不一定是每个线程都可以平均执行100个任务。尽管线程调度有记帐系统来尽可能让每个参与调度的线程执行的时间比较平均,但是也不可以百分百确保每个线程执行获取的任务数量都一模一样。


 四、Java当中有哪些线程池  


目前所见的线程池,本质上都是包装Java当中的一个原生类:ThreadPoolExecutor来实现的

下面将简单认识几个常见的:    

    ①Executors.newFixedThreadPool

       普普通通的线程池,没有什么其他额外的功能。仅仅是用于初始化指定数量的线程,然后添加任务,供自己初始化的线程来执行。


     ②Executors.newCacheThreadPool

       这个线程池,里面的线程数量是动态变换的。

       如果线程池当中的任务比较多,那么将会多初始化一些线程;如果任务比较少,那么将会少初始化一些线程


     ③Executors.newScheduledThreadPool

      这个线程池有点类似于"计时器",让任务拥有延时执行的功能。

      但是这些任务的具体执行是由线程池当中的线程执行。


     ④Executors.newSingleThreadExecutor

        这个线程池当中,只允许拥有一个线程。


五、线程池的工作过程

      ①判断线程池当中运行线程的数量是否到达核心线程数量corePoolSize

  如果线程池当中运行的线程数量没有到达核心线程数量,就会创建一个新的工作线程来执行任务。

  如果线程池当中运行线程数量>=corePoolSize,那么会进入②


       ②判断工作队列(存放任务的阻塞队列)是否已经满了

如果工作队列没有满,那么就把新提交的任务存放到这个工作队列当中,等待线程池当中的线程来执行这个任务。

如果工作队列也满了,那么会进入③


       ③再次创建新的线程

       再次创建新的工作线程,让新的工作线程来执行新增的任务。

      

       如果创建的新的线程让当前线程池的运行线程数量超出maximumPoolSize,将会触发拒绝策略。


总的图解:

  经典面试问题:已知CPU的核心数为N,那么需要的线程池的线程数量是多少呢?


        (1)对于CPU密集型的任务:

       每个线程都需要进行一系列的算术运算,可以理解为此时线程需要狂转CPU。

       那么,此时线程池的线程数量,最大不应该超过CPU的核心数量(N)。

        原因:

      CPU密集型的任务,意味着线程需要比较多的时间在CPU内核上面运行。

       那么,此时应该尽可能让线程都运行在CPU上面,不应该有比较多的线程调度次数。也就是,让每一个线程都尽可能都一直占用一个CPU内核,减少调度的次数,这样程序执行的效率会比较高。


       (2)对于IO密集型的任务

       每一个线程的工作都是等待IO,例如读写硬盘,读写网卡这些比较耗时的任务。

       当执行这些任务的时候,线程大部分是处于阻塞状态,不参与CPU的调度。

       这个时候,理论上线程数量远超过CPU的核心数量也是可以的。但是具体的线程数量,需要结合开发的实践来进行决定。


六、线程池ThreadPoolExecutor有哪些参数


    ①核心线程数量(corePoolSize)

       如果把一个线程池比作一家公司,那么corePoolSize可以理解为这个公司当中的正式员工的总数量。     

       即使这些线程没有在执行任务,也不会被"销毁".

       如果新添加任务的时候,当前运行的线程没有达到核心线程数量(corePoolSize),就会创建新的线程来执行添加的任务。


        并且,当线程当中运行的线程没有达到corePoolSize的时候,即使线程一直空闲着,也不会被销毁。

       我们执行下面这段代码,创建线程池的时候:

ExecutorService pool= Executors.newFixedThreadPool(10);

  指定的参数,就是这个corePoolSize 


    ②任务队列(runnableTaskQueue)

     用于存放执行等待任务的阻塞队列,这个任务队列生效的情况在于:

      运行的线程数量>=核心线程数量,就会把任务添加到这个队列当中。 

    对于阻塞队列的列举已经在这一篇文章当中说明:        Java阻塞队列_革凡成圣211的博客-CSDN博客https://blog.csdn.net/weixin_56738054/article/details/128533816?spm=1001.2014.3001.5501


为什么线程池当中需要使用阻塞队列 

  • 线程池创建线程需要获取mainlock这个全局锁,影响并发效率,阻塞队列可以很好的缓冲。
  • 另外一方面,如果新任务的到达速率超过了线程池的处理速率,那么新到来的请求将累加起来,这样的话将耗尽资源。

    ③最大线程数量(maximumPoolSize)

        一个公司当中,有正式的员工,也会有实习生/临时工。公司的总人数就是:正式员工的数量+实习生/临时工的总数量。同样地,可以理解为:一个线程池当中总的线程数量(maximunPoolSize)为:核心线程数量+空闲线程的数量。

        最大线程数量可以理解为,此时线程池已经无法再次容纳更多的线程了.

        如果在工作队列已经满了的情况下面,创建新的线程将使得当前运行的线程超出maximunPoolSize,将触发拒绝策略。

       反之,如果创建新的线程没有使得当前运行的线程超过maximunPoolSize,那么线程池就会继续创建新的线程来执行任务。

       


    ④keepAliveTime

       这个属性生效的时间在于,当运行的线程数量超过corePoolSize,并且当有线程处于空闲状态的时间超过KeepAliveTime之后,将会被销毁。


     ⑤unit

        空闲线程存活时间单位,也就是keepAliveTime的单位


     ⑥ThreadFactory

        线程工厂,线程池创建一个新的线程时候使用的"工厂",这个涉及到"工厂模式".


     ⑦RejectedExceptionHandler(饱和策略,也称为拒绝策略)

       当队列和线程池都已经满了,说明线程池处于饱和状态,那么必须采取一系列策略来处理新提交的任务。下面,一共有4种拒绝策略:

       (1)直接抛出异常


       (2)只用调用者所在线程来执行任务

       可以理解为,哪个线程在当前线程池满了之后再次提交任务,那么它提交的任务,不再交给线程池当中的线程执行,而是自己执行自己提交过的任务。


       (3)丢弃队列当中的最近一个任务

        线程池当中的任务都是被排好序的,这种拒绝策略就是丢掉正在执行的任务,执行最新提交的任务。


       (4)不处理最新提交的任务 

        直接丢弃线程池满了之后,新增的任务,不予执行。


七、设计一个简单的线程池       

      ①需要一个阻塞队列,保存添加的任务;

      ②构造方法指定线程池当中线程的数量;

      ③submit()方法往线程池当中提交任务(runnable):


      代码实现:

class MyThreadPool {/*** 阻塞队列,用来保存任务*/private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();/*** 创建n个线程* 线程数量@param n*/public MyThreadPool(int n) {//在这里创建for (int i = 0; i < n; i++) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {while (true) {try {//取出线程Runnable runnable = queue.take();//执行任务runnable.run();} catch (InterruptedException e) {e.printStackTrace();}}}});t.start();}}public void submit(Runnable runnable) {try {queue.put(runnable);} catch (InterruptedException e) {e.printStackTrace();}}
}

八、为什么阿里巴巴开发规范禁用Executors

最主要的原因:这些线程池都存在内存溢出(oom)的可能性:也就是"内存用完了"。

具体分析如下:

从构造方法分析

       关于这些参数的含义,已经在上面的文章当中提到了。

       下面,重点关注这一个参数:阻塞队列。这个参数的含义就是:当线程池的核心线程都处于非空闲的状态的时候,如果添加任务,就会把任务(Runnable)加入到这一个阻塞队列当中。

      从阻塞队列的长度分析

        然后,再看一下默认调用无参数的构造方法的时候,阻塞队列的最大长度:

这个参数的含义就是当前阻塞队列的最大长度

而这个最大长度,就是整形的最大值。也就意味着当前的阻塞队列的长度是Integer.MAX_VALUE

那么也就意味着,这样容易出现oom(Out of Memory Killer)也就是内存溢出。


http://chatgpt.dhexx.cn/article/pvETmhMM.shtml

相关文章

线程池(一)线程池的基本使用

一、线程池简介 线程池的概念 线程池就是首先创建一些线相衬&#xff0c;它们的集合称为线程池&#xff0c;使用线程池可以很好的提高性能&#xff0c;线程池在系统启动时既创建大量空闲的线程&#xff0c;程序将一个任务传给线程池。线程池就会启动一条线程来执行这个任务&…

线程池介绍及创建线程池的4种方式

1. 什么是线程池 Java中的线程池是运用场景最多的并发框架&#xff0c;几乎所有需要异步或并发执行任务的程序 都可以使用线程池。在开发过程中&#xff0c;合理地使用线程池能够带来3个好处。 第一&#xff1a;降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成…

线程池的使用

1.线程池使用场景 java中经常需要用到多线程来处理一些业务&#xff0c;我们非常不建议单纯使用继承Thread或者实现Runnable接口的方式来创建线程&#xff0c;那样势必有创建及销毁线程耗费资源、线程上下文切换问题。同时创建过多的线程也可能引发资源耗尽的风险&#xff0c;这…

线程池_线程池详解

1 线程池使用场景&#xff1f; java中经常需要用到多线程来处理一些业务&#xff0c;我们非常不建议单纯使用继承Thread或者实现Runnable接口的方式来创建线程&#xff0c;那样势必有创建及销毁线程耗费资源、线程上下文切换问题。同时创建过多的线程也可能引发资源耗尽的风险&…

Netty 线程池

Netty的线程池有什么样的特性 Java 原生线程池 Java 原生的线程池主要有三种&#xff1a;ThreadPoolExecutor、ScheduledThreadPoolExecutor、ForkJoinPool。 ThreadPoolExecutor 是最古老的类&#xff0c;我们通常说的线程池&#xff0c;也是指这个类。 ScheduledThreadPoo…

Linux —— 线程池

目录 一、什么是线程池 二、线程池的优点 三、线程池的应用 四、实现一个简单的线程池 五、单例模式 1. 饿汉实现方式 2. 懒汉实现方式 3. 单例模式实现线程池&#xff08;懒汉方式&#xff09; 六、其他常见的各种锁 一、什么是线程池 线程池是线程的一种使用模式。在…

线程池的实现原理

系统学习性&#xff0c;移步 IT-BLOG 线程池做的工作主要是控制运行的线程数量&#xff0c;处理过程中将任务放入队列&#xff0c;然后在线程创建后启动这些任务&#xff0c;如果线程数超过了最大数量超出数量的线程排队等候&#xff0c;等其他线程执行完毕&#xff0c;再从队列…

java——线程池

一、线程池 线程池可以看做是线程的集合。它的工作主要是控制运行的线程的数量&#xff0c;处理过程中将任务放入队列&#xff0c;然后在线程创建后 启动这些任务&#xff0c;如果线程数量超过了最大数量超出数量的线程排队等候&#xff0c;等其它线程执行完毕&#xff0c; 再…

java线程池(详解)

线程池介绍 线程池&#xff08;thread pool&#xff09;&#xff1a;一种线程使用模式。线程过多会带来调度开销&#xff0c;进而影响缓存局部性和整体性能。而线程池维护着多个线程&#xff0c;对线程统一管理。 线程池就是存放线程的池子&#xff0c;池子里存放了很多可以复…

Java线程池详解

本文包含知识点 线程池的使用场景分析线程池的创建及重要参数线程池实现线程复用的原理springboot中使用线程池Callabel与Runnable任务在基于spring体系的业务中正确地关闭线程池实现优先使用运行线程及调整线程数大小的线程池(线程池的优化)在java web项目中慎用Executors以及…

C++线程池

1.基础概念 线程池&#xff1a;一种线程的使用模式&#xff0c;线程过多会带来调度开销&#xff0c;进而影响缓存局部性和整体性。而线程池维护着多个线程&#xff0c;等待监督管理者分配可并行执行的任务。这样避免了在短时间内创建和销毁线程的代价。线程池不仅能够内核的充分…

线程池详解

成功不是将来才有的&#xff0c;而是从决定去做的那一刻起&#xff0c;持续累积而成。 目录 背景 线程池介绍 线程池使用 Executors 线程池如何关闭&#xff1f; 面试题 总结 背景 下面是一段创建线程并运行的代码: for (int i 0; i < 100; i) {new Thread(() -&…

线程池(通俗易懂)

目录 一、什么是线程池 二、创建线程池的方式 三、线程池的七大参数 四、四种拒绝策略 1.AbortPolicy() 2.CallerRunsPolicy() 3.DiscardPolicy() 4.DiscardOldestPolicy() 五、自定义一个线程池 1.场景描述 2.代码实现 一、什么是线程池 线程池其实就是一种多线程处理…

线程池研发学习笔记

线程池研发 线程池 线程池基础 概念介绍 1:什么是线程池 可以直接叙述,也可以对比连接池介绍 线程池其实就是一种多线程处理形式&#xff0c;处理过程中可以将任务添加到队列中&#xff0c;然后在创建线程后自动启动这些任务。这里的线程就是我们前面学过的线程,这里的任务就是…

线程池是什么?线程池(ThreadPoolExecutor)使用详解

点一点&#xff0c;了解更多https://www.csdn.net/ 本篇文章将详细讲解什么是线程池&#xff0c;线程池的参数介绍&#xff0c;线程池的工作流程&#xff0c;使用Executors创建常见的线程池~~~ 目录 点一点&#xff0c;了解更多 文章目录 一、线程池的概念 1.1线程池的目的…

写给小白看的线程池,还有10道面试题

如何搞定20k的面试小抄 为什么要用线程池呢&#xff1f; 下面是一段创建线程并运行的代码: for (int i 0; i < 100; i) {new Thread(() -> {System.out.println("run thread->" Thread.currentThread().getName());userService.updateUser(....);}).start…

线程池详解(通俗易懂超级好)

目标 【理解】线程池基本概念 【理解】线程池工作原理 【掌握】自定义线程池 【应用】java内置线程池 【应用】使用java内置线程池完成综合案例 线程池 线程池基础线程池使用线程池综合案例学员练习线程池总结 概念介绍 什么是线程池为什么使用线程池线程池有哪些优势 什么…

Java 多线程:彻底搞懂线程池

熟悉 Java 多线程编程的同学都知道&#xff0c;当我们线程创建过多时&#xff0c;容易引发内存溢出&#xff0c;因此我们就有必要使用线程池的技术了。 目录 1 线程池的优势 2 线程池的使用 3 线程池的工作原理 4 线程池的参数 4.1 任务队列&#xff08;workQueue&#x…

GridView概述

一、使用GridView以表格形式显示多张图片 GridView用于在界面上按行、列分布的方式来显示多个组件 二、使用GridView 1、java代码 import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.AdapterV…

Master-Detail GridView

梦幻版Master-Detail GridView(黄忠成) 2007-12-26 09:34 前面的Master-Detail GridView控件應用&#xff0c;相信你已在市面上的書、或網路上見過&#xff0c;但此節中的GridView控件應用包你沒看過&#xff0c;但一定想過&#xff01;請見圖4-8-63。 圖4-8-63 圖 4-8-64 你一…