java多线程(二)线程池

目录

java线程池

线程池应用场景:

如何创建线程池:

有什么区别:

不同线程池对应的应用场景

案例

输出结果


java线程池

Java线程池是一种预先创建一定数量的线程,并将任务提交给这些线程执行的机制。线程池可以避免频繁创建和销毁线程,提高程序的性能和响应速度。

为什么要创建线程池:

  1. 降低资源消耗:通过重复利用已创建的线程,避免线程的频繁创建和销毁,降低资源消耗。
  2. 提高响应速度:当任务到达时,任务不需要等待线程创建就能立即执行。
  3. 提高线程的可管理性:线程池可以进行统一的分配、调优和监控,有利于提高线程的可管理性。

线程池应用场景:

  1. 快速响应用户请求(IO密集型任务):如用户要查看商品信息,需要将商品维度的一系列信息聚合起来展示给用户。通过使用线程池,可以快速响应用户请求。
  2. 快速处理批量任务(CPU密集型任务):如统计某个报表,需要计算出全国各个门店中有哪些商品有某种属性,用于后续营销策略的分析。通过使用线程池,可以快速处理批量任务。

如何创建线程池:

Java中的线程池可以通过以下几种方式创建:

  1. Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。方法只有一个参数,即线程池的大小。
  2. Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。
  3. Executors.newSingleThreadExecutor:创建一个单个线程数的线程池,它可以保证先进先出的执行顺序。
  4. Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池。
  5. ThreadPoolExecutor:通过ThreadPoolExecutor类创建线程池,可以自定义线程池的参数,如核心线程数、最大线程数、队列类型等。

有什么区别:

使用 Executors 工厂类中的 newCachedThreadPool() 和 newFixedThreadPool() 方法:

优点:

- 使用起来比较简单,不需要自己手动管理线程池的状态;
- 可以快速创建线程池。

缺点:

- 无法自定义线程池的大小和饱和策略;
- 不够灵活。

使用 Executors 工厂类中的 newSingleThreadExecutor() 方法:

优点:

- 创建单个线程的线程池;
- 适用于需要顺序执行任务的场景。

缺点:

- 无法自定义线程池的大小和饱和策略;
- 不够灵活。

使用 ThreadPoolExecutor 类来创建线程池:

参数:

- corePoolSize:核心线程数
- maximumPoolSize:最大线程数
- keepAliveTime:线程存活时间
- unit:时间单位
- workQueue:任务队列
- threadFactory:线程工厂
- handler:饱和策略

优点:

- 可以根据实际情况来自定义线程池的大小、任务队列等;
- 可以灵活地设置饱和策略。

缺点:

- 对于不熟悉线程池的开发人员来说,使用起来比较麻烦;
- 需要手动管理线程池的状态。

  1. 核心线程数:指线程池中保持活动的最小线程数。如果线程池中的当前线程数小于核心线程数,则会在需要时创建新的线程。
  2. 最大线程数:指线程池中允许的最大线程数。如果队列满了,并且当前线程数已经达到最大线程数,则会在需要时创建新的线程。
  3. 队列类型:指用于存储待执行任务的队列类型。常见的队列类型有直接提交队列、有界队列、无界队列等。
  4. 拒绝策略:指当任务队列已满,且所有线程都在工作,但仍然无法处理新任务时的处理策略。常见的拒绝策略有抛出异常、拒绝任务、阻塞任务等。

以上几种方式分别对应不同的应用场景和需求。其中,Executors类提供了一种简单的方式来创建线程池,但它的参数相对固定,不适合进行复杂的配置。而ThreadPoolExecutor类则提供了更多的参数选项,可以灵活地配置线程池的各种属性,适用于更为复杂的场景。

不同线程池对应的应用场景

  1. Executors.newFixedThreadPool:适用于需要控制并发线程数量的场景,例如,当需要确保每个请求都能得到响应时,或者当请求量很大时,可以控制并发线程数量,避免系统过载。-------需要创建固定数量的线程来执行长时间运行的任务;
  2. Executors.newCachedThreadPool:适用于执行大量短期异步任务,例如,Web请求处理或短期的后台任务。这种线程池能快速创建和销毁线程,以应对大量突发任务。大量短时间的任务,如网络请求
  3. Executors.newSingleThreadExecutor:适用于需要保证任务按照提交顺序执行的场景,例如,当需要保证任务的串行执行时。需要顺序执行任务的场景,如一个线程代表一个用户请求
  4. Executors.newScheduledThreadPool:适用于需要定时或周期性执行任务的场景,例如,定时任务、定时备份等。
  5. ThreadPoolExecutor:适用于需要自定义线程池参数的场景,例如,需要根据任务的特性(如优先级、执行时间等)来调整线程池的配置。这种线程池提供了更多的灵活性和控制性。复杂的应用场景,需要根据实际情况来动态地调整线程池的大小和饱和策略,如一个 Web 服务器上服务对多个用户的请求时

案例

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;/*** 线程处理类*/
public class ExecutorProcessPool {private ExecutorService executor;private static ExecutorProcessPool pool = new ExecutorProcessPool();private final int threadMax = 10;private ExecutorProcessPool() {System.out.println("threadMax>>>>>>>" + threadMax);executor = ExecutorServiceFactory.getInstance().createFixedThreadPool(threadMax);}public static ExecutorProcessPool getInstance() {return pool;}/*** 关闭线程池,这里要说明的是:调用关闭线程池方法后,线程池会执行完队列中的所有任务才退出*/public void shutdown() {executor.shutdown();}/*** 提交任务到线程池,可以接收线程返回值** @param task* @return*/public Future<?> submit(Runnable task) {return executor.submit(task);}/*** 提交任务到线程池,可以接收线程返回值** @param task* @return*/public Future<?> submit(Callable<?> task) {return executor.submit(task);}/*** 直接提交任务到线程池,无返回值** @param task*/public void execute(Runnable task) {executor.execute(task);}}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;/*** 线程池构造工厂*/
public class ExecutorServiceFactory {private static ExecutorServiceFactory executorFactory = new ExecutorServiceFactory();/*** 定时任务线程池*/private ExecutorService executors;private ExecutorServiceFactory() {}/*** 获取ExecutorServiceFactory** @return*/public static ExecutorServiceFactory getInstance() {return executorFactory;}/*** 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。** @return*/public ExecutorService createScheduledThreadPool() {// CPU个数int availableProcessors = Runtime.getRuntime().availableProcessors();// 创建executors = Executors.newScheduledThreadPool(availableProcessors * 10, getThreadFactory());return executors;}/*** 创建一个使用单个 worker 线程的* Executor,以无界队列方式来运行该线程。(注意,如果因为在关闭前的执行期间出现失败而终止了此单个线程,* 那么如果需要,一个新线程将代替它执行后续的任务)。可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。与其他等效的* newFixedThreadPool(1) 不同,可保证无需重新配置此方法所返回的执行程序即可使用其他的线程。** @return*/public ExecutorService createSingleThreadExecutor() {// 创建executors = Executors.newSingleThreadExecutor(getThreadFactory());return executors;}/*** 创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用* execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60* 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。注意,可以使用 ThreadPoolExecutor* 构造方法创建具有类似属性但细节不同(例如超时参数)的线程池。** @return*/public ExecutorService createCachedThreadPool() {// 创建executors = Executors.newCachedThreadPool(getThreadFactory());return executors;}/*** 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads* 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务* ,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止* ,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。** @return*/public ExecutorService createFixedThreadPool(int count) {// 创建executors = Executors.newFixedThreadPool(count, getThreadFactory());return executors;}/*** 获取线程池工厂** @return*/private ThreadFactory getThreadFactory() {return new ThreadFactory() {AtomicInteger sn = new AtomicInteger();public Thread newThread(Runnable r) {SecurityManager s = System.getSecurityManager();ThreadGroup group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();Thread t = new Thread(group, r);t.setName("任务线程 - " + sn.incrementAndGet());return t;}};}
}

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;/*** 测试类*/
public class ExecutorTest {public static void main(String[] args) {ExecutorProcessPool pool = ExecutorProcessPool.getInstance();/* for (int i = 0; i < 200; i++) {Future<?> future = pool.submit(new ExcuteTask1(i+""));
//          try {
//              如果接收线程返回值,future.get() 会阻塞,如果这样写就是一个线程一个线程执行。所以非特殊情况不建议使用接收返回值的。
//              System.out.println(future.get());
//          } catch (Exception e) {
//              e.printStackTrace();
//          }}*/List<String> list = new ArrayList(Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "n", "m", "o", "p"));for (int i = 0; i < list.size(); i++) {String s = list.get(i);pool.execute(new ExcuteTask2(s + ""));}//关闭线程池,如果是需要长期运行的线程池,不用调用该方法。//监听程序退出的时候最好执行一下。List<String> list2 = new ArrayList(Arrays.asList("的", "飞", "个", "个", "3"));for (int i = 0; i < list2.size(); i++) {String s = list2.get(i);pool.execute(new ExcuteTask2(s + ""));}
//        pool.shutdown();}/*** 执行任务1,实现Callable方式*/static class ExcuteTask1 implements Callable<String> {private String taskName;public ExcuteTask1(String taskName) {this.taskName = taskName;}@Overridepublic String call() throws Exception {try {
//              Java 6/7最佳的休眠方法为TimeUnit.MILLISECONDS.sleep(100);
//              最好不要用 Thread.sleep(100);TimeUnit.MILLISECONDS.sleep((int) (Math.random() * 1000));// 1000毫秒以内的随机数,模拟业务逻辑处理} catch (Exception e) {e.printStackTrace();}System.out.println("-------------这里执行业务逻辑,Callable TaskName = " + taskName + "-------------");return ">>>>>>>>>>>>>线程返回值,Callable TaskName = " + taskName + "<<<<<<<<<<<<<<";}}/*** 执行任务2,实现Runable方式*/static class ExcuteTask2 implements Runnable {private String taskName;public ExcuteTask2(String taskName) {this.taskName = taskName;}@Overridepublic void run() {try {TimeUnit.MILLISECONDS.sleep((int) (Math.random() * 1000));// 1000毫秒以内的随机数,模拟业务逻辑处理} catch (Exception e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "-------------这里执行业务逻辑,Runnable TaskName = " + taskName + "-------------");}}
}

输出结果

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

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

相关文章

ExecutorService、Callable、Future实现有返回结果的多线程原理解析

原创/朱季谦 在并发多线程场景下&#xff0c;存在需要获取各线程的异步执行结果&#xff0c;这时&#xff0c;就可以通过ExecutorService线程池结合Callable、Future来实现。 我们先来写一个简单的例子—— public class ExecutorTest {public static void main(String[] ar…

Vulnhub项目:EMPIRE: BREAKOUT

一、靶机地址 靶机地址&#xff1a;Empire: Breakout ~ VulnHub 靶机介绍&#xff1a; 该靶机被定义为简单&#xff0c;但是如果没有找到&#xff0c;那就难度成中等了&#xff01; 二、渗透过程 老三样&#xff0c;发现目标&#xff0c;这里用 arp-scan 确定靶机 ip&#…

Java基础50题:14. 使用方法求最大值(2种方法)

概述 使用方法求最大值。 创建方法求两个数的最大值max2&#xff0c;随后再写一个求3个数的最大值函数max3。 要求&#xff1a; 在max3这个方法中&#xff0c;调用max2函数&#xff0c;来实现3个数的最大值计算。 方法一 【代码】 public class P14 {public static int max…

算法___

文章目录 算法两数之和 算法 两数之和 题目如下图&#xff1a; 我的答案如下图&#xff1a; 我采用的是最笨的思路&#xff0c;直接暴力的两次循环&#xff0c;第一次外循环是取数组的第一个元素&#xff0c;然后内循环会遍历数组后面除第一个的所有元素&#xff0c;然后和…

DDD架构思想专栏二《领域层的决策设计思想详解》

如果不了解DDD基本概念的读者可以去看这篇文章&#xff0c;传送门&#xff1a;DDD架构思想专栏一《初识领域驱动设计DDD落地》-CSDN博客 前言介绍 在上一章节介绍了领域驱动设计的基本概念以及按照领域驱动设计的思想进行代码分层&#xff0c;但是仅仅只是从一个简单的分层结…

【Flink系列三】数据流图和任务链计算方式

上文介绍了如何计算并行度和slot的数量&#xff0c;本文介绍Flink代码提交后&#xff0c;如何生成计算的DAG数据流图。 程序和数据流图 所有的Flink程序都是由三部分组成的&#xff1a;Source、Transformation和Sink。Source负责读取数据源&#xff0c;Transformation利用各种…

Remix IDE 快速开始Starknet

文章目录 一、Remix 项目二、基于Web的开发环境Remix 在线 IDE三、Starknet Remix 插件如何使用使用 Remix【重要】通过 Starknet by Example 学习一、Remix 项目 Remix 项目网站 在以太坊合约开发领域,Remix 项目享有很高的声誉,为各个级别的开发人员提供功能丰富的工具集…

JS中深拷贝与浅拷贝

定义 深拷贝&#xff08;Deep Copy&#xff09;和浅拷贝&#xff08;Shallow Copy&#xff09;是在编程中常用的两种对象复制方式。 浅拷贝&#xff08;Shallow Copy&#xff09;&#xff1a; 浅拷贝是创建一个新的对象&#xff0c;将原始对象的属性值复制到新对象中。如果属…

Smart Link和Monitor Link

Smart Link和Monitor Link简介 Smart Link&#xff0c;又叫做备份链路。一个Smart Link由两个接口组成&#xff0c;其中一个接口作为另一个的备份。Smart Link常用于双上行组网&#xff0c;提供可靠高效的备份和快速的切换机制。 Monitor Link是一种接口联动方案&#xff0c;它…

nodejs流

什么是流 stream 流是用于在 Node.js 中处理流数据的抽象接口。 node:stream 模块提供了用于实现流接口的 API。 什么是流数据 流数据是指一组顺序、大量、快速、连续到达的数据序列&#xff0c;一般情况下数据流可被视为一个随时间延续而无限增长的动态数据集合。流数据应用…

【keil备忘录】2. stm32 keil仿真时的时间测量功能

配置仿真器Trace内核时钟为单片机实际的内核时钟&#xff0c;需要勾选Enable设置&#xff0c;设置完成后Enable取消勾选也可以&#xff0c;经测试时钟频率配置仍然生效&#xff0c;此处设置为48MHZ: 时间测量时必须打开register窗口&#xff0c;否则可能不会计数 右下角有计…

第十四章 : Spring Boot 整合spring-session,使用redis共享

第十四章 &#xff1a; Spring Boot 整合spring-session,使用redis共享 前沿 本文重点讲述&#xff1a;spring boot工程中使用spring-session机制进行安全认证&#xff0c;并且通过redis存储session&#xff0c;满足集群部署、分布式系统的session共享。 基于SPringBoot 2.3.2…

[linux运维] 利用zabbix监控linux高危命令并发送告警(基于Zabbix 6)

之前写过一篇是基于zabbix 5.4的实现文章&#xff0c;但是不太详细&#xff0c;最近已经有两个小伙伴在zabbix 6上操作&#xff0c;发现触发器没有str函数&#xff0c;所以更新一下本文&#xff0c;基于zabbix 6 0x01 来看看效果 高危指令出发问题告警&#xff1a; 发出邮件告…

学好操作系统需要的前置知识

1. 态度&#xff1a;不要等一切都准备好了再前行 如果把一切你可能会说&#xff0c;没有这些基础知识&#xff0c;我每看一篇文章&#xff0c;知识就铺天盖地席卷过来&#xff0c;仿佛每一个知识点都准确地打在了自己的盲点上&#xff0c;这该怎么办呢&#xff1f; 我非常能理…

AI助力智慧农业,基于YOLOv8全系列模型【n/s/m/l/x】开发构建不同参数量级的识别系统

智慧农业随着数字化信息化浪潮的演变有了新的定义&#xff0c;在前面的系列博文中&#xff0c;我们从一些现实世界里面的所见所想所感进行了很多对应的实践&#xff0c;感兴趣的话可以自行移步阅读即可&#xff1a; 《自建数据集&#xff0c;基于YOLOv7开发构建农田场景下杂草…

05 JQuery基础入门

文章目录 一、jQuery介绍1. 简介2. 版本介绍3. 相关网站4. HTML引入方式 二、基础语法1. 顶级对象$2. 与DOM对象转化3. 选择器4. 事件5. 动画6. 修改样式7. 修改属性 一、jQuery介绍 1. 简介 jQuery是JavaScript编程语言底层库&#xff0c;它是一个快速&#xff0c;简洁的Jav…

ERPNext SQL 注入漏洞复现

0x01 产品简介 ERPNext 是一套开源的企业资源计划系统。 0x02 漏洞概述 ERPNext 系统frappe.model.db_query.get_list 文件 filters 参数存在 SQL 注入漏洞,攻击者除了可以利用 SQL 注入漏洞获取数据库中的信息(例如,管理员后台密码、站点的用户个人信息)之外,甚至在高权…

基于springboot实现的仿天猫商城项目

一、系统架构 前端&#xff1a;jsp | js | css | jquery 后端&#xff1a;springboot | mybatis-plus 环境&#xff1a;jdk1.7 | mysql | maven 二、代码及数据库 三、功能介绍 01. web端-首页 02. web端-商品查询 03. web端-商品详情 04. web端-购物车 05. web端-订单…

集合的几个遍历方法

1. 集合的遍历 1.0 创建集合代码 List<String> strList new ArrayList<>(); strList.add("huawei"); strList.add("xiaomi"); strList.add("tencent"); strList.add("google"); strList.add("baidu");1.1 fo…

复杂gRPC之go调用go

1. 复杂的gRPC调用 我们使用了一个较为复杂的proto文件&#xff0c;这个文件的功能主要是用来定位的&#xff0c;详细内容可以看代码中的注解 syntax "proto3"; //指定生成的所属的package&#xff0c;方便调用 option go_package "./"; package route…