Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >异步模式之工作线程

异步模式之工作线程

作者头像
一个风轻云淡
发布于 2023-10-15 03:10:50
发布于 2023-10-15 03:10:50
16900
代码可运行
举报
文章被收录于专栏:java学习javajava学习java
运行总次数:0
代码可运行
定义

让有限的工作线程(Worker Thread)来轮流异步处理无限多的任务。也可以将其归类为分工模式,它的典型实现 就是线程池,也体现了经典设计模式中的享元模式。

例如,海底捞的服务员(线程),轮流处理每位客人的点餐(任务),如果为每位客人都配一名专属的服务员,那 么成本就太高了(对比另一种多线程设计模式:Thread-Per-Message)

注意,不同任务类型应该使用不同的线程池,这样能够避免饥饿,并能提升效率

例如,如果一个餐馆的工人既要招呼客人(任务类型A),又要到后厨做菜(任务类型B)显然效率不咋地,分成 服务员(线程池A)与厨师(线程池B)更为合理,当然你能想到更细致的分工

饥饿

固定大小线程池会有饥饿现象

两个工人是同一个线程池中的两个线程

他们要做的事情是:为客人点餐和到后厨做菜,这是两个阶段的工作

  • 客人点餐:必须先点完餐,等菜做好,上菜,在此期间处理点餐的工人必须等待
  • 后厨做菜:没啥说的,做就是了

比如工人A 处理了点餐任务,接下来它要等着 工人B 把菜做好,然后上菜,他俩也配合的蛮好

但现在同时来了两个客人,这个时候工人A 和工人B 都去处理点餐了,这时没人做饭了,饥饿

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Test {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.submit(()->{
            System.out.println("点餐中...");
            Future<String> future = executorService.submit(() -> {
                return "宫保鸡丁1";
            });
            try {
                String s = future.get();
                System.out.println("上菜"+s);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });

        executorService.submit(()->{
            System.out.println("点餐中...");
            Future<String> future = executorService.submit(() -> {
                return "宫保鸡丁2";
            });
            try {
                String s = future.get();
                System.out.println("上菜"+s);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });

    }
}

运行结果如下: 点餐中... 点餐中... 上菜宫保鸡丁2 上菜宫保鸡丁1

 如果修改核心线程为3,即修改代码中

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ExecutorService executorService = Executors.newFixedThreadPool(3);

 则运行结果如下: 点餐中... 点餐中... 上菜宫保鸡丁2 上菜宫保鸡丁1

解决方法可以增加线程池的大小,不过不是根本解决方案,还是前面提到的,不同的任务类型,采用不同的线程池,例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Test {
    public static void main(String[] args) {
        ExecutorService orderExecutorService = Executors.newFixedThreadPool(1);
        ExecutorService cookExecutorService = Executors.newFixedThreadPool(1);
        orderExecutorService.submit(()->{
            System.out.println("点餐中...");
            Future<String> future = cookExecutorService.submit(() -> {
                return "宫保鸡丁1";
            });
            try {
                String s = future.get();
                System.out.println("上菜"+s);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });

        orderExecutorService.submit(()->{
            System.out.println("点餐中...");
            Future<String> future = cookExecutorService.submit(() -> {
                return "宫保鸡丁2";
            });
            try {
                String s = future.get();
                System.out.println("上菜"+s);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });

    }
}

运行结果如下:

 点餐中... 上菜宫保鸡丁1 点餐中... 上菜宫保鸡丁2

创建多少线程池合适

线程池的大小应根据具体的应用场景和系统需求来确定。以下是一些建议供参考:

  1. 考虑系统资源:线程池的大小应该与系统可用的资源相匹配。如果将线程池的大小设置得太大,会消耗过多的系统内存和CPU资源,导致系统性能下降;如果将线程池的大小设置得太小,可能无法充分利用系统资源,导致任务排队等待执行。
  2. 考虑任务类型:不同类型的任务对线程池的需求量不同。如果任务是CPU密集型(计算密集型),即任务在执行过程中主要消耗CPU资源,那么线程池的大小可以设置与CPU核心数相等或略大一些;如果任务是IO密集型(输入输出密集型),即任务在执行过程中主要消耗IO操作(如网络请求、文件读写等),那么线程池的大小通常可以设置较大,以便充分利用系统的IO能力。
  3. 考虑任务的响应时间:如果任务对响应时间要求较高,即需要快速响应用户请求,那么可以适当增加线程池的大小,以提高并发能力和响应速度。
  4. 考虑任务排队策略:线程池的大小还应考虑任务排队的策略。如果线程池使用有界队列作为任务缓冲区,当任务数量过多时,超出队列容量的任务将被拒绝执行;如果使用无界队列作为任务缓冲区,则线程池大小可以设置较大,以允许更多的任务排队等待执行。

总之,确定线程池大小需要综合考虑系统资源、任务类型、响应时间和任务排队策略等因素,并进行实际的性能测试和调优。根据实际情况不断调整线程池的大小,以达到最佳的性能和资源利用率。

CPU 密集型运算

通常采用 cpu 核数 + 1 能够实现最优的 CPU 利用率,+1 是保证当线程由于页缺失故障(操作系统)或其它原因 导致暂停时,额外的这个线程就能顶上去,保证 CPU 时钟周期不被浪费

I/O 密集型运算

CPU 不总是处于繁忙状态,例如,当你执行业务计算时,这时候会使用 CPU 资源,但当你执行 I/O 操作时、远程RPC 调用时,包括进行数据库操作时,这时候 CPU 就闲下来了,你可以利用多线程提高它的利用率。

经验公式如下

线程数 = 核数 * 期望 CPU 利用率 * 总时间(CPU计算时间+等待时间) / CPU 计算时间

例如 4 核 CPU 计算时间是 50% ,其它等待时间是 50%,期望 cpu 被 100% 利用,套用公式

4 * 100% * 100% / 50% = 8

例如 4 核 CPU 计算时间是 10% ,其它等待时间是 90%,期望 cpu 被 100% 利用,套用公式

4 * 100% * 100% / 10% = 40

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-09-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
JUC学习之共享模型之工具上之线程池浅学
ThreadPoolExecutor 使用 int 的高 3 位来表示线程池状态,低 29 位表示线程数量
大忽悠爱学习
2022/01/10
4310
JUC学习之共享模型之工具上之线程池浅学
JUC学习笔记——并发工具线程池
如果想要解除之前的饥饿现象,正确的方法就是采用Worker Thread模式为他们分配角色,让他们只专属于一份工作:
秋落雨微凉
2022/11/21
4080
JUC学习笔记——并发工具线程池
CompletableFuture 异步多线程,那叫一个优雅
虽然 Future 以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,我们必须使用Future.get()的方式阻塞调用线程,或者使用轮询方式判断 Future.isDone 任务是否结束,再获取结果。
程序员大彬
2023/03/01
1.6K0
CompletableFuture 异步多线程,那叫一个优雅
线程池遇到未处理的异常会崩溃吗?
在多线程编程中,线程池是提高性能和资源利用率的重要工具。Java 提供了 execute 和 submit 两种方法来提交任务到线程池。虽然它们看起来相似,但在实际使用中却有显著的区别。本文将详细介绍这两种方法的使用场景及其在处理异常时的行为差异。
用户11397231
2025/01/24
790
线程池遇到未处理的异常会崩溃吗?
面试官:线程池遇到未处理的异常会崩溃吗?
首先,这个问题考察的是你对线程池 execute 方法和 submit 方法的理解,在 Java 线程池的使用中,我们可以通过 execute 方法或 submit 方法给线程池添加任务,但如果线程池中的程序在执行时,遇到了未处理的异常会怎么呢?接下来我们一起来看。
磊哥
2024/09/13
2120
聊聊并发(五)——线程池
在使用线程时,需要new一个,用完了又要销毁,这样频繁的创建和销毁很耗资源,所以就提供了线程池。道理和连接池差不多,连接池是为了避免频繁的创建和释放连接,所以在连 接池中就有一定数量的连接,要用时从连接池拿出,用完归还给连接池,线程池也一样。   线程池:一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多 个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。
用户4268038
2021/11/18
2690
CompletableFuture实现异步编排
场景:电商系统中获取一个完整的商品信息可能分为以下几步:①获取商品基本信息 ②获取商品图片信息 ③获取商品促销活动信息 ④获取商品各种类的基本信息 等操作,如果使用串行方式去执行这些操作,假设每个操作执行1s,那么用户看到完整的商品详情就需要4s的时间,如果使用并行方式执行这些操作,可能只需要1s就可以完成。所以这就是异步执行的好处。
科技新语
2023/02/01
1.5K0
一次性解决老大难问题:线程治理 Futrue、Callable接口、CompletableFuture
线程治理最重要的是线程池了,之前我讲过,但是,还有两大法宝就是future 和 callable
Joseph_青椒
2023/08/26
9390
一次性解决老大难问题:线程治理 Futrue、Callable接口、CompletableFuture
Java线程池详解
构造一个线程池为什么需要几个参数?如果避免线程池出现OOM?Runnable和Callable的区别是什么?本文将对这些问题一一解答,同时还将给出使用线程池的常见场景和代码片段。
用户4283147
2022/10/27
3960
Java线程池详解
Juc并发编程12——2万字深入源码:线程池这篇真的讲解的透透的了
本文将介绍常见的线程池的使用方法,介绍线程池的参数、拒绝策略、返回参数获取以及定时调度。
半旧518
2022/10/26
1610
Juc并发编程12——2万字深入源码:线程池这篇真的讲解的透透的了
Java多线程最佳实践指南
最近正值秋招旺季,面试免不了问一些多线程的问题,而在Java多线程编程中,为了确保程序的稳定性和性能,我们需要遵循一系列的最佳实践。本文将介绍这些最佳实践,并提供代码示例来帮助理解。
灬沙师弟
2024/10/10
1910
Java多线程最佳实践指南
21.3 Java 线程池
线程是在一个进程中可以执行一系列指令的执行环境,或称运行程序。多线程编程指的是用多个线程并行执行多个任务。当然,JVM 对多线程有良好的支持。
acc8226
2022/05/17
3490
21.3 Java 线程池
11.JUC线程高级-线程池&Fork/Join
线程池: 提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度。
用户1212940
2022/04/13
2050
11.JUC线程高级-线程池&Fork/Join
Java ExecutorService:你真的了解它吗?
生动形象的比喻,ExecutorService 就像是一个管理者,你可以把任务交给它,它会根据需要创建线程,并且确保任务按照你的要求执行。
忆愿
2025/01/21
950
Java ExecutorService:你真的了解它吗?
Java线程池详解
构造一个线程池为什么需要几个参数?如果避免线程池出现OOM?Runnable和Callable的区别是什么?本文将对这些问题一一解答,同时还将给出使用线程池的常见场景和代码片段。
java架构师
2019/02/22
6590
Java线程池详解
线程池中线程抛了异常,该如何处理?
在实际开发中,我们常常会用到线程池,但任务一旦提交到线程池之后,如果发生异常之后,怎么处理? 怎么获取到异常信息?在了解这个问题之前,可以先看一下 线程池的源码解析,从源码中我们知道了线程池的提交方式:submit和execute的区别,接下来分别使用他们执行带有异常的任务!看结果是怎么样的!
码猿技术专栏
2023/05/01
6490
线程池中线程抛了异常,该如何处理?
CompletableFuture 让你的代码免受阻塞之苦
现在大部分的CPU都是多核,我们都知道想要提升我们应用程序的运行效率,就必须得充分利用多核CPU的计算能力;Java早已经为我们提供了多线程的API,但是实现方式略微麻烦,今天我们就来看看Java8在这方面提供的改善。
用户5546570
2020/11/26
8020
CompletableFuture 让你的代码免受阻塞之苦
ExecutorService 并发指南
在软件开发不断发展的世界中,有效管理并发任务的能力至关重要。传统的线程方法可能变得繁琐且容易出错,特别是在处理大量异步操作时。这时,ExecutorService 登场了:它是Java并发框架中一个强大的抽象,旨在简化和优化异步任务执行。
FunTester
2025/01/23
1750
ExecutorService 并发指南
线程池
package com.shi.juc; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * 一、线程池:提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度。 *
用户5927264
2019/09/17
7210
线程池
字节二面:线程池中线程抛了异常,该如何处理?
异常处理大家应该很熟了。但有些事务我们需要跑在线程池里,这种异常处理应该如何实现?
java思维导图
2023/11/02
3.8K0
字节二面:线程池中线程抛了异常,该如何处理?
相关推荐
JUC学习之共享模型之工具上之线程池浅学
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验