多线程之线程池使用

『与善仁』Appium基础 — 19、元素定位工具之UI Automator Viewer 文章目录(一)uiautomatorviewer定位工具1、uiautomatorviewer介绍2、uiautomatorviewer工具打开方式3、uiautomatorviewer布局介绍4、uiautomatorviewer工具的使用 之前我们在做web页面自动化的时候就说过,元素定位是页面自动化的第一步。 同理App自动化测试的第一步,也是元素的定位。 在APP中定位元素的话,需要借助一些元素定位工具。 APP中的元素定位工具大致有三种: uiautomatorviewer Appium Ins 阅读详情

常用实现多线程的方式

  1. 继承 Thread
  2. 实现 Runable
  3. 实现 Callable
  4. 使用ExecutorService、Future实现

为什么要用线程池

  1. 线程的创建、销毁都是需要消耗资源
  2. 线程创建的太多,也会导致过度消耗内存,切换时也会浪费大量时间
  3. 线程池有队列,可以存储待执行的任务
  4. 线程池可以统一管理线程
  5. 线程复用,还可以控制最大并发数

JUC

Jdk1.5之后加入了JUC,也就是java.util.concurrent,用官方的解释就是 ‘并发编程中通常有用的实用程序类’ 。java线程池是通过Executor框架实现的,先看Executor和Executors
在这里插入图片描述

Executors

先看Executors它里面有这些方法
在这里插入图片描述
其中常用的线程池有
newSingleThreadExecutor 、
newFixedThreadExecutor、
newCachedThreadExecutor、
newScheduledThreadPool
这四种有兴趣的同学可以看一下底层源码,但我们只需要弄清他的原理不建议使用(原因看阿里巴巴编程规范),直击底层会发现这些线程池最终都是调用
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
这是ThreadPoolExecutor类的构造方法,记住里面的七个参数,后面会详细注明每个参数的含义及运行的原理。而ThreadPoolExecutor类的顶级接口就是Executor,也就是我们接下来要说的。

Executor

Executor是线程池的顶级接口,但他却不是线程池,只是个执行线程的工具。我们先看看官方API文档怎么说的,
我们已知的子接口:
ExecutorService, ScheduledExecutorService
所有已知的实现类:
AbstractExecutorService,
ForkJoinPool(将大型任务分解为小任务的线程池),
ScheduledThreadPoolExecutor(支持定时调度的线程池),
ThreadPoolExecutor。
今天我们的主讲ThreadPoolExecutor。。
在这里插入图片描述

线程池的组成

线程池有四个部分组成:

  1. 线程池管理器,用于创建并管理线程
  2. 工作线程,线程池内的线程
  3. 任务接口:每个任务需实现的接口,永无线程调度及运行
  4. 任务队列:存放待处理的任务,提供缓存

线程池的状态

	private static final int COUNT_BITS = Integer.SIZE - 3;//workerCount所占位数
	private static final int CAPACITY   = (1 << COUNT_BITS) - 1;//workerCount上线
    // 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;//任务全部执行完毕,当前活动线程是0,即将进入终结
    private static final int TERMINATED =  3 << COUNT_BITS;//终结状态

ThreadPoolExecutor

我们看ThreadPoolExecutor类里面有四个构造方法,主要看
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
我们先来看看这个构造方法里面的七个参数的详细解释:

  1. corePoolSize: 核心线程数,指保留的线程池中线程数量的大小(不超过maximumPoolSize值 时,线程池中最多有corePoolSize 个线程工作)。
  2. maximumPoolSize : 指的是线程池的最多能拥有的线程数量(超过这个数量,任务将会存放指定队列)
  3. keepAliveTime : 指的是空闲线程结束的超时时间(超过corePoolSize部分的线程不工作时,过keepAliveTime 时间将停止该线程)。
  4. unit: 是一个枚举,表示 keepAliveTime 的单位(有NANOSECONDS, MICROSECONDS,MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS,7个可选值)。
  5. workQueue : 表示存放任务的队列(存放需要被线程池执行的线程队列)。
  6. threadFactory: 执行程序创建新线程时使用的工厂。
  7. handler: 拒绝策略,由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。

线程池工作过程

线程池是如何工作的呢,我们来好好整整ThreadPoolExecutor工作过程及七个参数何时用怎么用。
在这里插入图片描述

线程池刚创建是没有线程的,新任务到来不会立马启动,只有当调用execute() 方法添加一个任务时,线程池就会判断核心线程数corePoolSize是否已经达最大值,没有则创建线程执行任务,大于corePoolSize时就将任务塞到队列(workQueue )等待执行,当队列被塞满,会再次创建非核心线程执行任务,直到线程数达到maximumPoolSize,再进入的任务则会被拒绝策略 RejectedExecutionHandler 处理。线程会不断从队列中获取任务执行直到队列为空,有线程超过keepAliveTime 时间未工作则回收,直到只剩核心线程数工作。
举个生活例子解释,银行总共有10个窗口(maximumPoolSize),大厅有座位(workQueue ),银行平时固定开5个窗口(corePoolSize)办事,当5个窗口都被占满时,剩余的客人就只能在座位上等待,座位坐满了银行经理会将剩下的5个窗口也安排人办理事情,当10个窗口满了,座位也满了,这时候就让保安(RejectedExecutionHandler )安排后面来的客人。慢慢座位空了,窗口也逐渐开始没人,窗口长时间没人时银行经理就会安排减少窗口,直到只剩固定的五个窗口。

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
       	//工作线程数小于核心线程数,则调用addWorker创建线程工作
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 如果已开启的工作线程数大于或等于核心线程数,则把任务添加入堵塞队列
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 添加失败,则继续开启工作线程,如果已经开启线程数小于最大线程数,则添加成功,否则,使用指定的拒绝策略拒绝
        else if (!addWorker(command, false))
            reject(command);
    }

	/**
	 * 提交Runnable,指定返回值
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

    /**
     * 提交Runnable,指定返回值
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

    /**
     * 提交Callable
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

ThreadPoolExecutor参数的设置选择

其实关于参数的选择,ThreadPoolExecutor源码是有介绍的。

线程数大小的选择:
  1. 纯计算的任务多建议线程数为CPU数量或加一;
  2. 任务包含大量IO/网络等待等,线程数 = CPU 核数 × 目标 CPU 利用率 ×(1 + 平均等待时间 / 平均工作时间)
队列的选择:

在这里插入图片描述

  1. ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
  2. LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
  3. SynchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

这是三个阻塞队列,有兴趣可以了解下java的其他阻塞队列和非阻塞队列

拒绝策略 RejectedExecutionHandler的选择:

在这里插入图片描述

public enum RejectPolicy {

	/** 处理程序遭到拒绝将抛出RejectedExecutionException */
	ABORT(new ThreadPoolExecutor.AbortPolicy()),
	/** 放弃当前任务 */
	DISCARD(new ThreadPoolExecutor.DiscardPolicy()),
	/** 如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程) */
	DISCARD_OLDEST(new ThreadPoolExecutor.DiscardOldestPolicy()),
	/** 由主线程来直接执行 */
	CALLER_RUNS(new ThreadPoolExecutor.CallerRunsPolicy());

	private RejectedExecutionHandler value;

	private RejectPolicy(RejectedExecutionHandler handler) {
		this.value = handler;
	}

	/**
	 * 获取RejectedExecutionHandler枚举值
	 * 
	 * @return RejectedExecutionHandler
	 */
	public RejectedExecutionHandler getValue() {
		return this.value;
	}
}
  1. ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
  2. ThreadPoolExecutor.DiscardPolicy:丢弃无法处理的任务,也不抛异常。如果允许丢弃任务,这事最好的方案。
  3. ThreadPoolExecutor.DiscardOldestPolicy:也是丢弃任务,只不过是丢弃即将被执行的任务,并提交当前任务
  4. ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
  5. 若以上策略还不能满足需要,可以自己扩展 RejectedExecutionHandler 接口
工厂(threadFactory)的选择:

在这里插入图片描述
如果不是特别指定就选择默认的工厂,还有一个privilegedThreadFactory,返回用于创建新线程的线程工厂,这些新线程与当前线程具有相同的权限

    public static ThreadFactory defaultThreadFactory() {
        return new DefaultThreadFactory();
    }
    public static ThreadFactory privilegedThreadFactory() {
        return new PrivilegedThreadFactory();
    }

最后

其实想要玩转多线程,还得了解线程的三大特性,可以看看JMM(java内存模型),volatile关键字的作用,还有线程锁、线程调度等等。java api文档下的这三个包里面的类都可以看看
在这里插入图片描述

USB 网卡——RNDIS 控制消息流程 在其控制端点上获取控制消息时,设备必须在通信类接口的中断 IN 端点上返回通知,每当设备可以返回控制消息时,主机就会轮询该通知。需要注意的是,如果设备由于某种原因收到GET_ENCAPSULATED_RESPONSE,并且无法使用控制端点上的有效数据做出响应,则应返回设置为 0x00 的单字节数据包,而不是 stall 控制端点。下表中定义了此传输。收到 RESPONSE_AVAILABLE 通知后,主机使用下表中定义的GET_ENCAPSULATED_RESPONSE 传输从控制端点读取控制消息。 阅读详情

相关推荐

【HSCNN解析】HSCNN:从欠采样光谱图重建高光谱图的CNN网络

本文介绍HSCNN,是一个将欠采样光谱图重建为高光谱图的纯CNN网络,2017年提出。

上个大学哪有不疯的?硬撑罢了! 1619

带你认识多线程、线程池,并简单使用

线程池和多线程都是常用的并发处理技术,它们可以提高程序的效率和响应速度。线程池可以减少线程创建和销毁的开销,并控制并发数量,从而提高系统的性能。多线程可以并发执行程序中的不同任务,提高程序的效率和系统的吞吐量。

解决bug的路上 1319

利用运放设计简单有源滤波器(低通、高通、带通)

本文旨在帮助刚接触模电的同学快速设计一个实用可靠的运放,故我将不会说一些晦涩难懂的原理,只给出仿真电路图。

WeiTong0527的博客 3万+

深入理解多线程与线程池及其具体使用

多线程和线程池是并发编程中的重要概念,它们可以有效地提高程序的性能和响应速度。通过合理地使用多线程和线程池,可以更好地管理系统资源、提高系统的并发处理能力,并改善用户体验。因此,深入理解和掌握多线程和线程池的原理和用法对于提高软件开发的效率和质量至关重要。

weixin_42279822的博客 948

BK3296蓝牙音频SoC深度评测:如何用这颗芯片打造超低功耗TWS耳机?

本文深度评测BK3296蓝牙音频SoC在TWS耳机中的应用,重点解析其22nm工艺带来的超低功耗优势(A2DP模式仅2.8mA)和3×3mm封装设计。通过芯片架构分析、双麦降噪调优及开发套件使用指南,为工程师提供打造高性能TWS耳机的完整解决方案,显著提升续航和音频质量。

rice5的博客 654

20 CVPR Learning 3D Semantic Scene Graphs From 3D Indoor Reconstructions 学习笔记

3RScan是大规模真实世界的室内场景,特点是对478个自然改变的室内环境在不同的时间帧进行了1482 RGB-D scans,每一个scene都包括了一些位置被改变的物体。它和ScanNet数据集的区别主要在于ScanNet中的每一个采样环境都不相同,但是3RScan中存在采样环境相同但物体的位置不同的场景。Scene Graph Annotations 则是在3RScan的基础上给物体加入了场景图需要的关系、物体特征和类别标注,同时对3RScan进行了扩展,以此形成了场景图数据。

weixin_43508459的博客 1834

JAVA 多线程实现、线程池创建使用、多线程的异步操作

JAVA 多线程实现、线程池创建使用、多线程的异步操作

majunssz的博客 2494

链接文件及功能安全:S32K3如何将FLASH驱动放到RAM里面、RAM如何实现软件复位数据不丢失操作。

在通过RTD的SDK也好MCAL也好,始终存在一个问题,生成的代码除了看门狗模块,默认都是放在flash里面,按照正常逻辑来看没啥大的问题,思考一个问题:Flash放在0x100的位置,但是此时我们要操作0x100怎么办?那肯定不允许啊,但是将flash的驱动放在RAM里面就可以避免这个问题的产生。Flash的特性:PF0能操作PF1,当然PF1也能操作PF0的,以此类推,只要不操作自身,想怎么玩都可以。这也是一个物理特性吧。

梦想技术家 2310

线程池的作用? 数据库连接池?

1、限定线程的个数,不会导致由于线程过多导致系统运行缓慢或崩溃 2、线程池不需要每次都去创建或销毁,节约了资源、 3、线程池不需要每次都去创建,响应时间更快。 我们详细的解释一下为什么要使用线程池? 在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换

sulixu的博客 502

【软考】数据库系统工程师备考指南(一)

目录一、数据库系统工程师简介二、考试要求三、考试目标四、考试范围(一)信息系统知识(二)数据库系统设计与管理 一、数据库系统工程师简介 数据库系统工程师考试属于全国计算机技术与软件专业技术资格(水平)考试(简称计算机软件水平资格考试)中的一个中级考试。考试不设学历与资历条件,也不论年龄和专业,考生可根据自己的技术水平选择合适的级别合适的资格,但一次考试只能报考一种资格。考试采用笔试形式,考试实行全国统一大纲、统一试题、统一时间、统一标准、统一证书的考试办法。笔试安排在一天之内。 2022 年数据库系统工程

邓邓子的博客 3859

RT-DETR改进策略【Neck】| GFPN 超越BiFPN 通过跳层连接和跨尺度连接改进RT-DETR颈部网络

本文记录的是利用颈部结构优化RT-DETR的目标检测网络模型。利用改进后的颈部网络,通过跳层连接,避免了在进行反向传播时的梯度消失问题,并且引入跨尺度连接,可以实现不同级别和层次的特征充分融合,获取足够的高层语义信息和低层空间信息交换,从而在大规模变化场景下提高检测性能。GIRAFFEDET: A HEAVY-NECK PARADIGM FOR OBJECT DETECTION-GFPN包含了上述的跳层连接(dense-link和log2nlog _{2} nlog2​n-link)以及跨尺度连接(Quee

Limiiiing的博客 2228

SpringBoot 中如何使用通过线程池使用多线程

SpringBoot对java的线程池进行了封装, 可以通过配置直接使用线程池。 配置线程池 @Configuration @EnableAsync public class WebContextConfiguration { @Bean public Executor threadPoolTaskExecutor() { ThreadPoolTaskExe...

iuie_sl的博客 1010

华为OD机试真题 新系统2026-04-01 Java&Go【计算数列位置N的值】

如果其前面的M个元素中,存在值相同的元素,则该位置上的数值等于前面M个数中最大的数值与最小的数值之如果其前面的M个元素中,不存在值相同的元素,则该位置上的数值等于前面M个数中最大的数值与最小的数值之差;1、输入M、N两个数,则按照以下规则形成一个数列;两个整数,用逗号分割,分别表示M和N。一个整数,表示数列第N个位置上的数值。2、数列的前M个元素的值为1到M;请计算该数列第N个位置上的数值。N取值范围:1<= N<=50。M取值范围:3<=M<=10。

MISAYAONE的博客 2034

QT-多线程、线程池的使用

在进行桌面应用程序开发的时候, 假设应用程序在某些情况下需要处理比较复杂的逻辑,如果只有一个线程去处理,就会导致窗口卡顿,无法处理用户的相关操作。这种情况下就需要使用多线程,其中一个线程处理窗口事件,其他线程进行逻辑运算,多个线程各司其职,不仅可以提高用户体验,还可以提升程序的执行效率。

R_conqueror的博客 1737

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Java多线程——线程池使用

本文介绍一下Java多线程中线程池的使用方式。

qq_50867577的博客 1271

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

多线程-线程池的使用

当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。线程对象在不同的时期有不同的状态。那么 Java 中的线程存在哪几种状态呢?Java 中的线程/* 新建 */NEW ,/* 可运行状态 */RUNNABLE ,/* 阻塞状态 */BLOCKED ,/* 无限等待状态 */WAITING ,/* 计时等待 *//* 终止 */TERMINATED;// 获取当前线程的状态。

2302_81141145的博客 1057

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Spring Boot 多线程池统一配置与使用教程

推荐实践建议说明拆分多线程池根据业务类型独立配置,防止互相干扰统一构建器避免重复代码异常封装器所有任务包装为,防止吞异常监控工具开发/线上查看线程使用情况若使用@Async,必须开启。

weixin_47766420的博客 808

java多线程及线程池使用

Java多线程及线程池的使用Java多线程一、Java多线程涉及的包和类二、Java创建多线程的方式三、Java线程池1. 创建线程池ThreadPoolExecutor的7个参数2. 线程池的执行流程3. 线程池的使用示例1)用java原生的线程池类 ThreadPoolExecutor2)用Spring推出的线程池工具 ThreadPoolTaskExecutor4. 获取多线程的运行结果—使用CompletableFuture1)CompletableFuture概述2)CompletableFutu

NA_imu的博客 879

JAVA多线程——线程池的使用

JAVA多线程——线程池 问题:经常创建和销毁使用量特别大的资源时,比如并发情况下的线程,对性能影响很大。 解决:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建和销毁、实现重复利用。 好处: 提高响应速度(减少了创建新线程的时间) 减低资源消耗(重复利用线程池中的线程) 便于线程管理 可以设置线程池的属性 corePoolSize:核心池的大小 maximumPoolSize: 最大线程数 keepAliveTime:线程没有任务时最多保持多久时间后会终止

万里顾一程的博客 562

Python中多线程和线程池的使用方法

上述代码中,我们定义了一个process函数,它接受一个列表作为参数,对列表中的每个元素进行运算,并将结果保存到另一个列表中。在上面的代码中,我们首先定义了一个GetHtmlThread类,继承自threading.Thread类,然后在类的构造函数中传入需要爬取的URL。在主程序中,我们创建了5个线程,每个线程都执行task函数,并传入不同的参数。但是,这种单线程爬取的方式效率较低,因为在获取HTML代码的时候需要等待响应,而在等待响应的过程中CPU会空闲,无法充分利用计算机的性能。

weixin_47831992的博客 4817

java多线程编程 线程池的使用

Java中的多线程编程是现代软件开发中不可或缺的一部分,尤其是在处理高性能计算、并发请求和异步任务时。线程池是Java多线程编程中的一个重要概念,它可以显著提高程序的性能和响应速度。下面我们详细探讨线程池的基本原理、如何在Java中使用线程池以及一些最佳实践。

yuehua00的专栏 1020

多线程-线程池配置及使用

 在开发过程中,如高并发场景、批量处理任务、记录日志等场景。多线程降低资源消耗,避免频繁线程创建/销毁的开销。提高响应速度。可限制并发线程数(如核心/最大线程数配置),防止资源耗尽导致系统崩溃。

qq_36833673的博客 416

java线程池和多线程的使用详解

ThreadPoolExecutor线程池位于 java.util.concurrent包下,是 Java 中用于实现线程池的一种基础类。

qq_42872720的博客 1592

Java多线程及线程池的使用(记录)

Java多线程: 可以通过new Thread(runnable).start()开启子线程,但是实际应用中这种开启多线程的方法在线程数非常多的情况下不一定能实现期望的功能,主要是在重复创建和销毁线程时消耗了大量的资源,导致多线程的效率反而没有单线程效率高。 通过线程池Executors.newFixedThreadPool()对创建的线程进行复用,减少了资源的消耗。 ExecutorSe...

yikunhan的博客 4465

python pyqt5 多线程池的使用案例

python pyqt5 多线程池的使用案例说明 说明 本代码来源于一个项目中的一部分,这里只展示了多线程部分 使用线程池进行网络的一些操作 # -*- coding: utf-8 -*- """ 登陆模块多线程 """ from PyQt5.QtCore import QRunnable, QObject, QThreadPool, QThread from core.logicalCode...

清风冷吟 9969
下一篇: MySQL加索引语句不加锁
氢氧根离子
博客等级 码龄15年 0粉丝 2原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值