Java 线程知识笔记 (四) Executor与ThreadPool 其二

JAVA 并发编程之十:线程模型2:Executor框架 + 线程池ThreadPool 的四种类型 前言 1. Executor框架包括:Executor,Executors,ExecutorService,CompletionService,Future,Callable,FutureTask等。 2. 线程|任务 区别: 实现Runnable的类应该被看作一项任务,而不是一个线程。在Java多线程中我们一定要有一个明确的理解,任务和线程是不同的概念。可以使用线程(Thread)执行任务(... 阅读详情

前言

上一篇【Java 线程知识笔记 (三)】我们从源码里面探究了一个线程在线程池中是如何执行的,由于线程池有一个很大的框架,仅仅通过这点东西就说明白线程池,尤其是对于初学者来说更是难以理解。因此这篇将会对源码讲解中遗漏的一些内容,比如线程池的状态,以及线程池中线程是在哪里创建的等等做一个补漏,同时对线程池的基本内容进行一个概念性的阐述。更多线程知识内容请点击【Java 多线程和锁知识笔记系列】

线程池的优点

上一篇中对线程池和单线程的效率做了一个对比的例子,同时开启10000个线程对一个list进行操作的时候,线程池的效率几乎是创建线程的100倍。之所以会有这样一个大的差距,是因为消耗资源的线程创建和线程运行解耦了,创建与调度交给了线程池,而外部(我们)只要关心如何运行就可以了。线程作为一个稀缺资源,从此不再是一次性的,而是可以复用了。其实可以看出线程池的核心思想就是:把宝贵的资源放到一个池子中,每次使用都从里面获取,用完之后又放回池子供其他人使用。

线程池的框架

线程池的框架很大,下图是Java中整个线程池的类图。Executor接口是整个线程池的顶层接口,ExecutorService接口则声明了众多操作线程池的方法, ThreadPoolExecutor类则是我们最常用的去初始化线程池的类,它和AbstractExecutorService类一起封装实现了很多常用的方法,比如submit()、executor()、shutdown(), invoke()等等。

在这里插入图片描述

从上面的图中可以看到,我们经常使用的Executors类并没有出现在线程池这个框架的类图里面,明明是一个和线程池息息相关的类为什么没有呢?因为Executors类是一个工具类,里面所有的方法都是静态方法,为了实现Java中默认的不同功能的线程池而使用的。老话说的好:工具人不配有姓名,Executors类就是这么一个工具人。

线程池的创建

当然我们也可以根据自己的需求去实现ThreadPoolExecutor类,或者做类似Executors这种工具人出来,但是我们的讲解还是要从Java中默认的内容出发。Executors类的官方文档上的说明如下,其中包含了很多不同种类的创建线程池方法,并返回各种需要的类型。

public class Executors extends Object 工厂和工具方法Executor ,
ExecutorService , ScheduledExecutorService ,
ThreadFactory和Callable在此包中定义的类。 该类支持以下几种方法:
• 创建并返回一个ExecutorService设置的常用的配置设置的方法。
• 创建并返回一个ScheduledExecutorService的方法, 其中设置了常用的配置设置。
• 创建并返回“包装”ExecutorService的方法,通过使实现特定的方法无法访问来禁用重新配置。
• 创建并返回将新创建的线程设置为已知状态的ThreadFactory的方法。
• 创建并返回一个方法Callable出的其他闭包形式,这样他们就可以在需要的执行方法使用Callable 。

常用的线程池

Executors里面的方法很多有兴趣可以自己去看【Java API传送门】。多归多,但是常用的也就那么几个,根据创建的方式不同可以分为下面几个大类:

名字返回值说明
newFixedThreadPool(int nThreads)ExecutorService创建一个固定大小的线程池,其中运行的线程共享一个无边界的队列。可以类比为数组,超出数量的任务要在等待队列中等待。
newCachedThreadPool()ExecutorService创建一个缓冲的线程池。根据需求在其中创建线程,一旦线程被创建,此线程即可被复用。可以类比为只能增加容量的变长数组,如果超出当前容量则创建一个新的线程。
newSingleThreadExecutor()ExecutorService创建一个使用单一工作线程的Executor,其运行时共享无边界队列。单例线程池按照不同的次序完成不同的任务,比如上篇例子中就是用这个进行的list添加,提高线程的重用性,才会有相差100倍的运行效果。
newScheduledThreadPool(int corePoolSize)ScheduledExecutorService计划线程池,这种线程池可以用来delay任务,或者执行定时任务。

话说Executors是工具人,因为最终都会调用到ThreadPoolExecutor去实现上面不同的的功能,只不过传入的参数不同而已,正是因为传递的参数不同才造成了连接池的表现不同。ThreadPoolExecutor构造方法已经在上篇中详细说过,不多说。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}


public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
}

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
    return new DelegatedScheduledExecutorService
        (new ScheduledThreadPoolExecutor(1)); //不用怀疑这里最终也是跳到ThreadPoolExecutor
}

线程池的生命周期

在之前的讲解Thread的博客中说过线程的生命周期,线程池同样也是有生命周期的,而且上篇中的源码部分已经就出现了线程池的状态判断。由于篇幅没有详细介绍,这里就说一下线程池的生命周期。线程池的状态定义在ThreadPoolExecutor里面,下面是官方源码中的注释。

/** 官网定义的状态和说明
*   RUNNING:  Accept new tasks and process queued tasks
*   SHUTDOWN: Don't accept new tasks, but process queued tasks
*   STOP:     Don't accept new tasks, don't process queued tasks,
*             and interrupt in-progress tasks
*   TIDYING:  All tasks have terminated, workerCount is zero,
*             the thread transitioning to state TIDYING
*             will run the terminated() hook method
*   TERMINATED: terminated() has completed
*
* The numerical order among these values matters, to allow
* ordered comparisons. The runState monotonically increases over
* time, but need not hit each state. The transitions are:
* 官网定义的状态转换
* RUNNING -> SHUTDOWN
*    On invocation of shutdown(), perhaps implicitly in finalize()
* (RUNNING or SHUTDOWN) -> STOP
*    On invocation of shutdownNow()
* SHUTDOWN -> TIDYING
*    When both queue and pool are empty
* STOP -> TIDYING
*    When pool is empty
* TIDYING -> TERMINATED
*    When the terminated() hook method has completed
*/

private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

状态描述:

状态名称状态名称状态说明
RUNNING运行状态RUNNING可以接受任务执行队列里的任务。也就是说线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0。
SHUTDOWN关闭状态处于SHUTDOWN 状态时,线程池不接收新任务,但是可以继续处理已添加到队列的已有任务。
STOP停止状态STOP状态,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
TIDYING整理状态当线程池中所有的任务已终止,任务数量为0时,线程池会变为TIDYING状态,并且运行钩子方法terminated()。
TERMINATED终止状态terminated()运行完成,线程池就会处于这种状态,此状态时线程池彻底终止。

注:要注意的是terminated()在ThreadPoolExecutor类中是空的。若用户想在线程池变为TIDYING时,进行相应的处理,可以通过重载terminated()方法来实现。


状态转换:线程池的状态之间的转换和线程类似。
在这里插入图片描述


COUNT_BITS 与 CAPACITY :
解释完状态的转换,就要解释COUNT_BITS的作用了。Integer.SIZE是32表示Integer是32位整型,我们一共只有5个状态,犯得着用Integer.SIZE去表示吗?首先说COUNT_BITS是什么:COUNT_BITS表示线程数量。COUNT_BITS = Integer.SIZE - 3也就是说最多有29位表示线程数量。那么后面的int RUNNING = -1 << COUNT_BITS就好理解了:状态用int型表示,左移29位就是状态,一共32位,高3位是状态,剩下29位就是线程数量。用高3位标识-1(111)、0(000)、1(001)、2(010)、3(011),因此线程池默认的最大容量CAPACITY 就是29位能表示的最大值2^30-1= ‭536870911‬。


线程池的安全关闭

一般来说,线程池不会关闭,因为用到线程池的场景会反复的调用线程,因此会维持一定数量的线程等待请求。但是如果某些需求需要手动关闭的的时候,就需要我们手动去维护了,但是安全的关闭线程池也是一个要注意的点。上面我们知道只有处于TERMINATED才表示真正关闭了线程池,因为为了保证线程池内所有的线程都安全的结束,我们需要等待线程池处于TERMINATED的状态才可以,比如:

ExecutorService service= Executors.newSingleThreadExecutor();
service.shutdown();
while(service.isTerminated()){
    //使用一个循环空转,直到线程池状态处于TERMINATED为止
}
System.out.println("pool is closed");

线程的复用

说到最后还有一个关键点,线程池是如何做到线程复用的。上一篇说到线程池中所有的任务都会被转换成Worker类对象执行,直接去Worker进入构造方法。

private boolean addWorker(Runnable firstTask, boolean core) {
    /**略**/
    Worker w = null;
    try {
        w = new Worker(firstTask); //直接进入这里
        final Thread t = w.thread; //这里的线程也是拿的Worker对象的
        if (t != null) {
            /**略**/
            if (workerAdded) {
                t.start(); //启动线程
                workerStarted = true;
            }
        }
    }
         /**略**/
    return workerStarted;
}

进入后发现thread是通过一个newThread()方法构建出来的,但是传入的是this,也就是Worker这个内部类对象本身,接着往下走会进入默认的线程工厂中。

Worker(Runnable firstTask) {
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask; //task传值
    this.thread = getThreadFactory().newThread(this);//创建核心线程
}

这个方法在Executors.DefaultThreadFactory#newThread()这里。

public Thread newThread(Runnable r) {
    Thread t = new Thread(group, r,
                          namePrefix + threadNumber.getAndIncrement(),
                          0);
    if (t.isDaemon())
        t.setDaemon(false);
    if (t.getPriority() != Thread.NORM_PRIORITY)
        t.setPriority(Thread.NORM_PRIORITY);
    return t;
}

进来以后发现这里还是new出来一个线程再用,那线程池说好的线程复用呢?其实这里new出来的Worker里的线程是线程池的核心线程。因为当线程被拿出来以后,调用了start()方法启动了这个new出来的核心线程,所以我们继续去Worker.run()里面。

public void run() {
    runWorker(this);
}

接着往runWorker()里走。

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    /**略**/
    boolean completedAbruptly = true;
    try {
        while (task != null || (task = getTask()) != null) {
            /**略**/
            try {
                /**略**/
                try {
                    task.run(); //执行线程逻辑
                } catch (RuntimeException x) {
                /**略**/
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

可以看到这里task被拿出来,然后经过while循环。点睛之处就在这个while循环的条件里,只要task不是空的之就行,但是如果task是空的那么就从getTask()里面取数据。getTask()正是从阻塞队列workQueue中取任务的方法。这样线程池中的线程永远都不会停下来,源源不断地从队列中取出任务执行。那么如果创建一个核心线程数为2,最大线程数为3的线程池执行逻辑就是这样的:

  1. 任务1进来,开一个线程池核心线程1,直接执行。
  2. 任务2进来,再开一个核心线程2,直接执行。
  3. 任务3进来,此时发现核心线程已经都开起了,而且都被占用了,把任务3放进队列。
  4. 核心线程1执行任务1结束,经过while循环使用getTask()拿出任务3执行。
  5. 任务n进来,发现核心线程满了,尝试放进队列,发现队列也满了,直接创建一个非核心线程f1,直接执行。
  6. 任务n+1进来,发现核心线程满了,队列满了,非核心线程满了,报错。

如此核心线程永远不停下来,永远空转从队列中拿任务执行直到线程池被杀掉。而非核心线程是有空转超时时间的,因此到时间就自己结束了。这就是线程池中线程复用的本质。

总结

到此线程池的内容基本就结束了,这篇博客对上一篇源码分析做了一个知识点的补充,以及最终把线程池中的线程复用从源码角度和逻辑上梳理清楚。希望这些内容对各位读者理解线程池有所帮助。

【AUCell打分】:评估一个基因集在单细胞转录组的每个细胞中特定的活性程度 AUCell使用曲线下面积来计算输入基因集的一个有意义的基因子集是否在每个细胞的表达基因中富集。AUC 分数在所有细胞中的分布允许探索特征的相对表达。由于评分方法是基于排名的,因此 AUCell 与基因表达单位和归一化程序无关。此外,由于细胞是单独评估的,因此可以很容易地应用于更大的数据集。 阅读详情

相关推荐

PaddleOCR在医疗票据识别中的实战:从部署到业务字段提取

本文详细介绍了PaddleOCR在医疗票据识别中的实战应用,重点阐述了其离线部署方案与PP-OCRv3模型选型优势。针对医疗票据敏感数据不出本地的合规要求,文章提供了从环境搭建、模型配置到核心字段提取策略的完整流程,并分享了性能优化与系统集成的实践经验,有效解决了医疗票据自动化录入的难题。

weixin_29271053的博客 236

代码随想录刷题Day51 | 309. 最佳买卖股票时机含冷冻期 | 714. 买卖股票的最佳时机含手续费

代码随想录刷题Day51 | 309. 最佳买卖股票时机含冷冻期 | 714. 买卖股票的最佳时机含手续费

DUM1615的博客 434

Win10下ISE14.7安装避坑全记录:从License加载失败到ChipScope连不上,我踩过的雷都在这了

本文详细记录了在Win10系统下安装ISE14.7时遇到的常见问题及解决方案,包括License加载失败、IMPACT闪退、ChipScope连接故障等。通过实际案例和步骤解析,帮助用户避开安装陷阱,确保FPGA开发工具顺利运行。特别针对license问题提供了兼容性设置和文件修改的实用技巧。

weixin_42683394的博客 225

买卖股票动态规划python_fucking-algorithm/动态规划系列/团灭股票问题.md at master · jasonpython/fucking-algorithm · GitHub...

#团灭 LeetCode 股票买卖问题很多读者抱怨 LeetCode 的股票系列问题奇技淫巧太多,如果面试真的遇到这类问题,基本不会想到那些巧妙的办法,怎么办?**所以本文拒绝奇技淫巧,而是稳扎稳打,只用一种通用方法解决所用问题,以不变应万变**。这篇文章用状态机的技巧来解决,可以全部提交通过。不要觉得这个名词高大上,文学词汇而已,实际上就是 DP table,看一眼就明白了。PS:本文参考自[英...

weixin_31304817的博客 352

CS231n+assignment2(一)

cs231n作业二详解+代码

qq_56551150的博客 2411

数据分析必问:Pandas面试题及参考答案

DataFrame是Pandas库中的一个核心数据结构,它类似于Excel中的表格,可以存储不同类型的数据。在Pandas中,抽样操作通常很简单,但是需要根据数据的特点和分析的需求来选择合适的抽样方法。数据标准化和归一化是数据预处理中常用的技术,目的是将数据调整到一个合适的范围内,以消除不同特征量纲和数值范围的差异,从而保证模型训练的有效性。CSV文件是一种文本文件,其中的数据以逗号分隔。不平衡数据集指的是数据集中各类别样本的数量差异较大的情况,这可能导致模型训练出现偏差,影响模型的泛化能力。

大模型大数据攻城狮的专栏 3009

代码随想录算法训练营第五十一天 买卖股票问题总结

和前面的题目比起来不算难,主要是把情况分类想清楚。

m0_64144465的博客 231

RK3588开发笔记-麦克风阵列多pdm通道合并成一个声卡

在音频设备的开发中,特别是在多通道音频数据处理场景中,如何将多个PDM(Pulse Density Modulation)通道整合成一个声卡设备,是一个非常具有挑战性的问题。RK3588芯片作为一款性能强大的平台,提供了对多个音频通道的支持。本文将介绍如何在RK3588上将多个PDM通道合并成一个麦克风阵列声卡,从而在Linux系统下统一管理和使用,提高多通道音频的同步性。在本文中我们主要介绍使用2通道回采+4通道mic声音采集构成一个6通道声卡,加以回声消除实现一个多麦克风阵列。

flypig has a dream! 1926

Java通过Executors创建线程池的四种方法

执行一个异步任务你还只是new Thread吗? 那你就out了,new Thread有很多的弊端: a. 每次new Thread新建对象性能差。 b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。 c. 缺乏更多功能,如定时执行、定期执行、线程中断。 让我们来看看Java提供的线程池的使用方法: Java通过Executors提供四种线程池...

weixin_38158701的博客 727

论文必备 - YOLO26热力图可视化,支持指定模型,指定显示层,设置置信度,以及9种可视化实现方式

本文带来的是YOLO26热力图可视化功能,支持指定模型,指定显示层,设置置信度,以及9种可视化实现方式。我们经常看到一些论文里绘制了不同的热力图,一方面能够直观的感受其模型的有效性,另一方面也丰富了论文内容。特别是在使用了注意力模块的网络中,热力图就可以验证注意力机制是否真正聚焦到了预期的重要特征上,以便对模型的有效性和合理性进行评估。例如Centralized Feature Pyramid for Object Detection这篇文章中展示的,就很能够表达作者改进后的模型相比之前模型的优越性。在项目

Limiiiing的博客 433

咨询进阶——解读40页公司战略解码方法【附全文阅读】

战略解码是将企业战略转化为可执行计划的关键过程,强调垂直和水平一致性原则。通过KPI指标设计和目标分解,确保战略在各部门和岗位有效落地。解码过程包括战略澄清、目标牵引、绩效分解和承诺签订,输出战略解码图等文件。不同部门根据责任定位(如利润中心、绩效中心)采用差异化考核方式,实现战略有效执行。

cdfunlove的博客 150

为什么要用Executors.defaultThreadFactory().newThread(run);创建线程?

Executors /** * Creates a thread pool that reuses a fixed number of threads * operating off a shared unbounded queue. At any point, at most * {@code nThreads} threads will be act...

weixin_33795743的博客 471

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Java高并发(四)——ThreadPool,线程复用

        前边我们讲述了:Java高并发——了解并行世界、Java高并发——多线程基础、Java高并发——多线程协作,同步控制 。从1,线程是什么?为什么需要多线程?2,Java对多线程的基础操作:线程的状态扭转,线程的创建、终止、中断、等待和通知、挂起和执行、等待结束和谦让,volatile关键字,线程组进行分类管理,守护线程,线程优先级,线程共享资源安全和synchronized进行控制...

学会改变自己——才能突破 1万+

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Java 线程知识笔记 (三) Executor与ThreadPool 其一

基本上已经把Java中线程有关的基础知识说了个大概。考虑到开启线程是一个开销很大的事情,如果每个子线程都在使用的时候做初始化等待分配等等一系列的事情,就会非常的影响程序的响应速度。为了解决这个问题,Java也是从1.5开始提出了线程池(ThreadPool)的概念。本篇就会着重对线程池进行一个讲解。

Smallc0de的博客 619

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Java多线程系列——Executor

Java多线程

飞影铠甲的博客 2627

java 线程池与executor_详解Java线程池和Executor原理的分析

详解Java线程池和Executor原理的分析线程池作用与基本知识在开始之前,我们先来讨论下“线程池”这个概念。“线程池”,顾名思义就是一个线程缓存。它是一个或者多个线程的集合,用户可以把需要执行的任务简单地扔给线程池,而不用过多的纠结与执行的细节。那么线程池有哪些作用?或者说与直接用Thread相比,有什么优势?我简单总结了以下几点:减小线程创建和销毁带来的消耗对于Java Thread的实现,...

weixin_30856775的博客 191

Java基础入门·多线程·线程池ThreadPool篇

​​​​​​​​​​​​​​前言在上一篇的文章中,我们初步了解Thread类的用法第一次书写多线程程序,算是完成一个Java学习的里程碑接下来我们继续进入多线程的学习线程池ThreadPool,目的就是提高效率。new Thread().start(),线程是内存中的一个独立的方法栈区,JVM没有能力开辟内存空间和OS交互而线程会频繁地与OS交互浪费资源,tips:后面学到的数据库连接池也是类似的效果jdk5开始内置线程池Executor类。

冰小九的博客 350

ThreadPool 线程池

文章目录ThreadPool 线程池线程池简介(三大方法,七大参数,四大策略)Executor类线程池参数说明拒绝策略(重点)Executors线程池的方法与创建newCachedThreadPool(常用)(可扩容)newFixedThreadPool(常用)(固定)newSingleThreadExecutor(常用)(单个)线程池的提交方式execute()submit()线程池的关闭方式 ThreadPool 线程池 三大方法: newSingleThreadExecutor(常用)(单个)–&

m0_54202121的博客 5139

java中ThreadPool的介绍和使用

java中ThreadPool的介绍和使用 Thread Pool简介 在Java中,threads是和系统的threads相对应的,用来处理一系列的系统资源。不管在windows和linux下面,能开启的线程个数都是有限的,如果你在java程序中无限制的创建thread,那么将会遇到无线程可创建的情况。 CPU的核数是有限的,如果同时有多个线程正在运行中,那么CPU将会根据线程的优先级进行轮循,给每个线程分配特定的CPU时间。所以线程也不是越多越好。 在java中,代表管理ThreadPool的接口有两个

vx539413949的博客 772

线程池之ThreadPool与ForkJoinPool

网上对Java线程池都有很多非常具体的解析,我概念性进行总结下,如有错误,可与我联系修改。 1.1ThreadPool Executor 一个线程池包括以下四个基本组成部分:  1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务; 2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务...

weixin_30325793的博客 566

ThreadPool线程池

ThreadPool线程池为什么要用线程池线程池如何使用编码实现:线程池7大重要参数线程池底层工作原理线程池用哪个?生产中如设置合理参数线程池的拒绝策略在工作中单一的/固定数的/可变的三种创建线程池的方法哪个用的多?超级大坑代码 为什么要用线程池 例子:10年前单核CPU电脑,假的多线程,像马戏团小丑玩多个球,CPU需要来回切换。现在是多核电脑,多个线程各自跑在独立的CPU上,不用切换,效率高。 线程池的优势: 线程池做的工作只要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,

Hello World 2597

在Java中Executor和Executors有什么不同?一次搞定!

Executor是Java中用于执行任务的核心接口,而Executors是一个工具类,提供了多种线程池创建方式。Executor定义了任务执行的基本规范,而Executors则负责具体实现和管理线程池。通过 Executors 可以轻松创建固定大小、缓存或定时的线程池,满足不同场景的需求。掌握它们的区别对理解Java多线程编程框架至关重要。

yanpeng2580的博客 908

Java多线程-马士兵高并发视频教程笔记

Java多线程基础 基础 创建线程 Java中创建线程主要有三种方式: 继承Thread类创建线程类 定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体 创建Thread子类的实例,即创建了线程对象 调用线程对象的start()方法来启动该线程 通过Runnable接口创建线程类 定义runnable接口...

dfvmudb8855的博客 2930

Java 异步编程——Java内置线程池(Executor 线程池)

默认情况下,只有当线程池中的线程数大于 corePoolSize 时,keepAliveTime 才会起作用,直到线程池中的线程数不大于 corePoolSize,即当线程池中的线程数大于 corePoolSize 时,如果一个线程空闲的时间达到 keepAliveTime,则会终止,直到线程池中的线程数不超过 corePoolSize。线程管理部分是消费者,它们被统一维护在线程池内,根据任务请求进行线程的分配,当线程执行完任务后则会继续获取新的任务去执行,最终当线程获取不到任务的时候,线程就会被回收。

longool的博客 1733

java 线程池 ThreadPool

Java 线程池 ThreadPool 问题: 单开线程的方式:new Thread(() -> {…})。已经很简单了,我为什么要用线程池。如果你需要成千上百个呢,这就需要用有一个统一的管理了。 线程池: 它有什么好处呢,减少线程的创建、销毁。反复利用这几个线程来执行所有新老任务。 线程池的源码,下面解读每个参数 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,

huangjiangmin 1105
上一篇: Java 线程知识笔记 (三) Executor与ThreadPool 其一
下一篇: Java 线程知识笔记 (五) synchronized 用法与原理分析
Smallc0de
博客等级 码龄15年 148粉丝 147原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值