【JavaEE】线程池

news2024/9/28 23:33:30

目录

线程池概念

线程池优点

使用线程池

Executor接口:

ThreadPoolExecutor类:

构造方法

Executors工厂类:

工厂方法

线程池中的常用方法

线程池的工作流程

线程池的状态

RUNNING

SHUTDOWN

STOP

TIDYING

TERMINATED

简单实现线程池


线程池概念

线程池:把一个或多个线程通过统一的方式进行调度和重复使用的技术。相当于往一个池里放入一些线程,要使用了,就拿出来,用完了就还回去。


线程池优点

线程池相较于之前创建/销毁线程(操作系统实现),变成了取出来使用,不用了就还回去(用户代码实现),这样的方法更为高效,降低了资源消耗

由于在操作系统中许多情况是位置的,而有了线程池就会让我们掌握主动权,从而就提高了线程的可管理性和任务执行的响应速度


使用线程池

有关并发编程的使用,大多都在java.util.concurrent这个包下面,简称 juc 。

Executor接口:

所有有关线程池使用的类都是实现了该接口。


ThreadPoolExecutor类

使用这个类,就可以使用线程池了。

构造方法

这个类的构造方法有四个,其参数较为复杂。

对于这四个构造方法,可以看到第四个构造方法的参数最多,下面详细介绍一下参数的意义。

参数意义
int corePoolSize

核心线程数量

核心线程是线程池当中一直保存的线程。

int maximumPoolSize

最大线程数量

线程池中的线程 = 核心线程 + 临时线程

该参数就是二者之和

如果当前任务比较多,核心线程不够用,那么就可以多创建一些临时线程干活;如果当前任务少,并且持续了一段时间了,此时就可以销毁一些临时线程。

这个最大数量没有确定值。但是根据具体场景大致可以分为两类:

①CPU密集型

此时每个线程执行的任务都是很吃CPU的,此时就算搞再多的线程有没有用。设置成CPU内核数即可。

②IO密集型

此时每个线程执行的任务都是在等待IO(读写硬盘、读写网卡等),此时线程处于阻塞状态,不参与CPU的调度,这样就可以尽可能的多搞一些线程。

上面两种情况都是极端情况,实际上我们可以根据这两种情况作为参考,试一些数来选出最优情况。

long keepAliveTime

临时线程最大空闲时间

当临时线程空闲时间超过这个值时,此时就会被销毁。

TimeUnit unit

上述空闲时间的单位

枚举类型

NANOSECONDS : 纳秒

MICROSECONDS :微秒

MILLISECONDS : 毫秒

SECONDS : 秒

MINUTES : 分

HOURS : 小时

DAYS : 天

BlockingQueue<Runnable> workQueue

线程池的任务队列

此处使用阻塞队列,如果有任务就take()成功,没有就阻塞,不用一直take()

ThreadFactory threadFactory

创建线程

这是一个接口,new时需要实现

线程池中创建的线程由该它重写方法

Thread newThread(Runnable r);

线程池中的线程都是前台线程。

RejectedExecutionHandler handler

拒绝策略

拒绝策略是当线程池的任务队列满了之后,如果再往里面添加任务会有什么样的行为。标准库提供了四种拒绝策略。下图所示

①直接抛出异常

②多出来的任务,谁加的,谁负责执行

③丢弃最早的任务,给新任务腾地方

④丢弃最新的任务,保持不变

 代码演示:

import java.util.concurrent.*;

public class ThreadDemo29 {

    public static void main(String[] args) {

        ThreadPoolExecutor threadPoolExecutor =
                new ThreadPoolExecutor(8, 16, 20L,
                        TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>(8),
                        new ThreadFactory() {
                            @Override
                            public Thread newThread(Runnable r) {
                                Thread thread = new Thread(r);
                                return  thread;
                            }
                        },
        new ThreadPoolExecutor.CallerRunsPolicy());

        for (int i = 0; i < 10; i++) {
            threadPoolExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行该任务!");
                }
            });
        }

    }

}

可以看出,使用该类实现线程池是较为繁琐的。通常我们不使用该类实现线程池,而是使用一个工厂类。


Executors工厂类:

工厂类:简单的理解为用普通方法代替构造方法。

Executors里的工厂方法把上面的ThreadPoolExecutor类的构造方法加工了一下,使其变得更为简单好用给。

工厂方法

下面四种常用的方法都是静态方法,返回类型为 ExecutorService 

方法说明
Executors.newFixedThreadPool(int nThreads)创建含有 nThreads个 线程的线程池
Executors.newCachedThreadPool()创建的线程池中的线程数量不定,任务与线程数量成正相关
Executors.newScheduledThreadPool(int corePoolSize)

创建含有 corePoolSize个 核心线程的线程池

类似于之前的定时器,让任务延时执行。扫描线程的任务由线程池自己完成。

Executors.newSingleThreadExecuto()创建只有一个线程的线程池

代码演示

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadDemo27 {

    public static void main(String[] args) {

        ExecutorService pool1 = Executors.newFixedThreadPool(8);
        ExecutorService pool2 = Executors.newCachedThreadPool();
        ExecutorService pool3 = Executors.newScheduledThreadPool(2);
        ExecutorService pool4 = Executors.newSingleThreadExecutor();


        for (int i = 0; i < 5; i++) {
            pool1.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行任务!");
                }
            });
        }

        for (int i = 0; i < 5; i++) {
            pool2.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行任务!");
                }
            });
        }

        for (int i = 0; i < 5; i++) {
            pool3.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行任务!");
                }
            });
        }

        for (int i = 0; i < 5; i++) {
            pool4.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行任务!");
                }
            });
        }

    }

}

 


线程池中的常用方法

方法说明
Future<?>    submit(Runnable task)

执行提交的任务

返回值  是Future接口

当任务执行期间,我可以干其他事情,当任务结束后,返回值给Future,我想什么时候回来取结果都可以

参数类型  Runnable类型的任务

void    execute(Runnable command)

执行提交的任务

返回值  为空  与上面的submit的区别

参数类型  Runnable类型的任务

void    shutdown()

启动有序关闭 

先提交的任务先被执行,同时不接受任何新任务  不会报错,执行完成后正常退出

List<Runnable>    shutdownNow()

尝试立刻停止任务

如果有还在执行的任务,会抛出 java.lang.InterruptedException: sleep interrupted 异常

如果没有,则返回等待执行的任务列表

boolean    isShutdown()

判断线程池是否关闭

true  ---  关闭

false ---  未关闭

boolean    isTerminated()

判断线程池是不是完成所有任务而关闭

true  ---  完成所有任务关闭

false  ---  为完成所有任务关闭


线程池的工作流程

 

线程池的状态

线程池有五种状态。

RUNNING

线程池一旦被创建,就处于 RUNNING 状态,任务数为 0,能够接收新任务,对已排队的任务进行处理。

SHUTDOWN

不接收新任务,但能处理已排队的任务。调用线程池的 shutdown() 方法,线程池由 RUNNING 转变为 SHUTDOWN 状态。

STOP

不接收新任务,不处理已排队的任务,并且会中断正在处理的任务。调用线程池的 shutdownNow() 方法,线程池由(RUNNING 或 SHUTDOWN ) 转变为 STOP 状态。

TIDYING

①SHUTDOWN 状态下,任务数为 0, 其他所有任务已终止,线程池会变为 TIDYING 状态,会执行 terminated() 方法。线程池中的 terminated() 方法是空实现,可以重写该方法进行相应的处理。

②线程池在 SHUTDOWN 状态,任务队列为空且执行中任务为空,线程池就会由 SHUTDOWN 转变为 TIDYING 状态。

③线程池在 STOP 状态,线程池中执行中任务为空时,就会由 STOP 转变为 TIDYING 状态。

TERMINATED

线程池彻底终止。线程池在 TIDYING 状态执行完 terminated() 方法就会由 TIDYING 转变为 TERMINATED 状态。


简单实现线程池

代码注释中详解。

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

class MyThreadPool {
    // 使用阻塞队列保存任务
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();

    // 构造方法中先创建几个线程
    public MyThreadPool (int n) {
        //
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(() -> {
                while (true) {
                    try {
                        // 一直从阻塞队列中拿任务
                        // 如果没有任务就自动阻塞
                        Runnable runnable = queue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            t.start();
        }
    }

    // 提交任务给线程池中的阻塞队列
    public void submit(Runnable task) {
        try {
            queue.put(task);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ThreadDemo28 {

    public static void main(String[] args) {

        MyThreadPool myThreadPool = new MyThreadPool(10);
        for (int i = 0; i < 10; i++) {
            myThreadPool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行任务!");
                }
            });
        }

    }

}


有什么错误评论区指出。希望可以帮到你。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/186494.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android10 开机向导流程

最近在弄开机向导&#xff0c;网上查了查&#xff0c;基本都是参照系统的​​​​​​Provision应用来做的&#xff0c;而且还要将apk打包到系统目录下的pri-app目录下&#xff0c;打包到其他目录下不行&#xff0c;参照着做是没问题&#xff0c;但是好奇为什么要这么做&#x…

esp32连接阿里云物联网平台进行MQTT通信

前提&#xff1a;IDE是采用arduino IDE&#xff0c;arduino使用的库是pubsubclient 开发板可以使用esp32&#xff08;esp8266也是一样的&#xff09; 已经学会pubsubclient库的基本使用 使用pubsubclient 库连接阿里云物联网平台 const char* ssid "........"; c…

java ssm校园勤工俭学助学志愿者兼职系统 idea maven

本论文主要论述了如何使用JSP技术开发一个校园勤工俭学兼职系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述校园勤工俭学兼职系统的当前背景以及系统开发的…

使用CCProxy+Proxifier实现代理

目录1.使用场景2.什么是网络代理&#xff1f;3.CCProxy3.1 说明3.2 下载安装3.3 使用说明4.Proxifier4.1 说明4.2 下载安装4.3 使用说明4.4 Proxifier CPU占用率高问题解决1.使用场景 很多时候当我们访问某个网络&#xff08;例如&#xff1a;校园网、企业网&#xff09;&#…

射频识别技术|期末考试知识点|重点题目|第1讲_RFID

课堂笔记 1.RFID技术 标签(芯片、天线、封装) 读写器 中间件和系统软件 公共服务体系 2.IC&ID

使用原始命令编译打包部署springboot-demo项目

目录简介源文件介绍编译编译restful-common编译manual-springboot打包&部署&执行jar命令介绍不打包直接运行打普通jar包&#xff0c;通过java -jar运行打fat jar通过java -jar打war&#xff0c;通过部署至tomcat运行纯手工命令开发打包部署的缺点参考简介 本文将使用j…

CUDA编程笔记(7)

文章目录前言共享内存的合理使用数组归约计算使用全局内存的计算引入线程块中的同步函数使用共享内存计算静态共享内存使用动态共享内存性能比较避免共享内存的bank冲突使用共享内存进行数组转置bank概念性能比较总结前言 cuda共享内存的合理使用。 共享内存的合理使用 共享…

TF-A移植

1.对tf-a源码进行解压 $> tar xfz tf-a-stm32mp-2.2.r2-r0.tar.gz 2.打补丁 $> for p in ls -1 ../*.patch; do patch -p1 < $p; done 3.配置交叉编译工具链 将Makefile.sdk中EXTRA_OEMAKE修改为 EXTRA_OEMAKECROSS_COMPILEarm-linux-gnueabihf- 4.复制设备树…

linux 部署jmeter

一、linux 安装jdk Java Downloads | Oracle 二、 linux上传jmeter 2.1 上传jmeter jmeter 下载地址&#xff1a; Apache JMeter - Download Apache JMeter 注意&#xff1a; 我先在我本地调试脚本&#xff08;mac环境&#xff09;&#xff0c;调试完成后&#xff0c;再在…

前端首屏优化

一. 打包分析 在 package.json 中添加命令 “report”: “vue-cli-service build --report” 然后命令行执行 npm run report&#xff0c;就会在dist目录下生成一个 report.html 文件&#xff0c;右键浏览器中打开即可看到打包分析报告。 二. 路由懒加载 component: () >…

MacOS - steam 蒸汽平台安装教程,带你躲避高仿网站的陷阱

MacOS - steam 蒸汽平台安装教程 MacOS 其实也是可以安装 Steam 平台的&#xff0c;虽然 steam 上的大多游戏暂时都不支持 MacOS&#xff0c;但还是有一些游戏可以玩的&#xff0c;而且近几年支持 MacOS 的游戏也是越来越多了。另外现在高仿网站特别多&#xff0c;所以才有了这…

transformer库使用

Transformer库简介 是一个开源库&#xff0c;其提供所有的预测训练模型&#xff0c;都是基于transformer模型结构的。 Transformer库 我们可以使用 Transformers 库提供的 API 轻松下载和训练最先进的预训练模型。使用预训练模型可以降低计算成本&#xff0c;以及节省从头开…

Grafana 系列文章(三):Tempo-使用 HTTP 推送 Spans

&#x1f449;️URL: https://grafana.com/docs/tempo/latest/api_docs/pushing-spans-with-http/ &#x1f4dd;Description: 有时&#xff0c;使用追踪系统是令人生畏的&#xff0c;因为它似乎需要复杂的应用程序仪器或 span 摄取管道&#xff0c;以便 ... 有时&#xff0c;使…

SurfaceFlinger学习笔记(七)之SKIA

关于Surface请参考下面文章 SurfaceFlinger学习笔记(一)应用启动流程 SurfaceFlinger学习笔记(二)之Surface SurfaceFlinger学习笔记(三)之SurfaceFlinger进程 SurfaceFlinger学习笔记(四)之HWC2 SurfaceFlinger学习笔记(五)之HWUI SurfaceFlinger学习笔记(六)之View Layout Dr…

react 实现表格列表拖拽排序

问题描述 在项目开发中&#xff0c;遇到这样一个需求&#xff1a;需要对表格里面的数据进行拖拽排序。 效果图如下所示&#xff1a; 思路 安装两个插件&#xff1a; react-sortable-hoc &#xff08;或者 react-beautiful-dnd&#xff09;array-move npm install --save r…

59 多线程环境普通变量作为标记循环不结束

前言 最近看到这篇例子的时候, [讨论] 内存可见性问题, 吧其中的 demo 拿到本地来跑 居然 和楼主一样, testBasicType 这里面的这个子线程 居然 不结束了, 卧槽 我还以为 只是可能 用的时间稍微长一点 哪知道 直接 无限期执行下去了, 然后 另外还有一个情况就是 加上了 -…

Segmenter论文解读

Segmenter: Transformer for Semantic Segmentation 论文&#xff1a;[2105.05633] Segmenter: Transformer for Semantic Segmentation (arxiv.org) 代码&#xff1a;[rstrudel/segmenter: ICCV2021] Official PyTorch implementation of Segmenter: Transformer for Semanti…

vue3+ts error TS7053:

在远程仓库拉取线上正常运行的项目&#xff0c;编译之后出现报错出现问题&#xff0c;逐步排查node版本是否与别人一致2.检查node_modules是否与别人一致检查到这一步就发现了是因为依赖版本不一致导致的原因因为目前vue-tsc等依赖更新频繁把这两个依赖的版本号锁死&#xff0c…

vuex

目录 1、什么是vuex 2、vuex的工作方式 3、vuex的使用场景 4、工作流程&#xff1a;View -> Actions -> Mutations -> State -> View 5、vuex的核心API ​ &#xff08;1&#xff09;state&#xff1a; ​ &#xff08;2&#xff09;mutations ​ &#xff…

Pinia的使用(以vue3+ts+vite为例)

文章目录Pinia1. 安装2. 引入vue33. 初始化store仓库4. 修改state5. 解构store6. store中的方法和计算属性&#xff08;actions、getters&#xff09;7. API7.1 $reset7.2 $subscribe 和 $onAction8. 插件案例&#xff1a;持久化插件Pinia Pinia官方文档 Pinia GitHub地址 1…