并发学习26--多线程 异步模式之工作线程

定义:让有限的工作线程(Worker Thread)来轮流异步处理无限多的任务。线程池便是这种模式。

饥饿

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

若一个线程池有两个线程,能够处理两种任务。但是两种任务间有先后顺序。若来一个任务的时候,线程A先处理,然后需要线程B在线程A内帮助处理后续问题。当来两个任务时,线程A,B同时处理这两个任务,但是没有线程处理后续问题。这时便出现了饥饿现象。

当两个线程处理一个任务时:
import lombok.extern.slf4j.Slf4j;import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;@Slf4j(topic = "TC47")
public class TC47 {static final List<String> foods = Arrays.asList("地三鲜","锅包肉","小炒肉","菠萝饭");static Random random = new Random();static String cooking() { return foods.get(random.nextInt(foods.size()));}public static void main(String[] args) {ExecutorService pool = Executors.newFixedThreadPool(2);//result: 中间有线程2帮助做饭//16:09:36.117 [pool-1-thread-1] DEBUG TC47 - 开始点单了....//16:09:36.148 [pool-1-thread-2] DEBUG TC47 - 开始做饭了....//16:09:36.148 [pool-1-thread-1] DEBUG TC47 - 上菜了: 小炒肉....pool.execute(()->{log.debug("开始点单了....");Future<String> food = pool.submit(() -> {log.debug("开始做饭了....");return cooking();});try {log.debug("上菜了: {}....",food.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}});}
}
当两个线程处理两个任务时--出现饥饿
import lombok.extern.slf4j.Slf4j;import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;@Slf4j(topic = "TC47")
public class TC47 {static final List<String> foods = Arrays.asList("地三鲜","锅包肉","小炒肉","菠萝饭");static Random random = new Random();static String cooking() { return foods.get(random.nextInt(foods.size()));}public static void main(String[] args) {ExecutorService pool = Executors.newFixedThreadPool(2);//result: 线程1,2同时点单,没有线程帮助做饭,出现饥饿//16:13:21.932 [pool-1-thread-2] DEBUG TC47 - 开始点单了....//16:13:21.932 [pool-1-thread-1] DEBUG TC47 - 开始点单了....pool.execute(()->{log.debug("开始点单了....");Future<String> food = pool.submit(() -> {log.debug("开始做饭了....");return cooking();});try {log.debug("上菜了: {}....",food.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}});pool.execute(()->{log.debug("开始点单了....");Future<String> food = pool.submit(() -> {log.debug("开始做饭了....");return cooking();});try {log.debug("上菜了: {}....",food.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}});}
}
避免饥饿

不同类型的任务应使用不同类型的线程池

import lombok.extern.slf4j.Slf4j;import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;@Slf4j(topic = "TC48")
public class TC48 {static final List<String> foods = Arrays.asList("地三鲜","锅包肉","小炒肉","菠萝饭");static Random random = new Random();static String cooking() {return foods.get(random.nextInt(foods.size()));}public static void main(String[] args) {ExecutorService waiterPool = Executors.newFixedThreadPool(1);ExecutorService cookPool = Executors.newFixedThreadPool(1);waiterPool.execute(()->{log.debug("开始点单....");Future<String> food = cookPool.submit(() -> {log.debug("开始做菜....");return cooking();});try {log.debug("上菜了: {}",food.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}});waiterPool.execute(()->{log.debug("开始点单....");Future<String> food = cookPool.submit(() -> {log.debug("开始做菜....");return cooking();});try {log.debug("上菜了: {}",food.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}});}
}

合理创建线程池

  • 线程池过小会导致不能充分利用系统资源,容易产生饥饿现象
  • 线程池过大会导致更多的线程上下文切换,占用更多内存,影响性能
CPU密集型运算

适合做数据分析

线程数=CPU核数+1

I/O密集型运算

当I/O密集运算时,可以利用闲置的CPU。

 任务调度线程池

Timer 

Timer执行定时功能但是task2受task1的Sleep影响。

import lombok.extern.slf4j.Slf4j;import java.util.Timer;
import java.util.TimerTask;
@Slf4j(topic = "TC49")
public class TC49 {public static void main(String[] args) {Timer time = new Timer();TimerTask task1 = new TimerTask() {@Overridepublic void run() {log.debug("Task1...");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}};TimerTask task2 = new TimerTask() {@Overridepublic void run() {log.debug("Task2...");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}};log.debug("start");time.schedule(task1,1000);time.schedule(task2,1000);}
}
 ScheduleExecutorService
schedule()

若任务中发生异常,它不会抛出异常也不会在控制台里打印出来,需要我们自己进行try/catch 捕获或这throw。

import lombok.extern.slf4j.Slf4j;import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;@Slf4j(topic = "TC50")
public class TC50 {public static void main(String[] args) {ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);//延迟1S执行pool.schedule(()->{log.debug("Taks1....");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}log.debug("Finish task1...");},1, TimeUnit.SECONDS);pool.schedule(()->{log.debug("Taks2....");log.debug("Finish task2...");},1, TimeUnit.SECONDS);}
}
SchedulelAtFixedRate() 执行时间会抵消delay时间
//result: 如果有sleep, scheduleAtFixedRate的delay会被第一次执行的时间抵消掉。//task3 第一次执行2S完后,抵消了第二次的delay 1S,所以第二次就直接在2S后执行。
//15:54:14.730 [pool-1-thread-1] DEBUG TC50 - Taks3....
//15:54:16.755 [pool-1-thread-1] DEBUG TC50 - Taks3....
//15:54:18.762 [pool-1-thread-1] DEBUG TC50 - Taks3....
//15:54:20.766 [pool-1-thread-1] DEBUG TC50 - Taks3....//任务,初始延迟时间,延迟周期,时间单位pool.scheduleAtFixedRate(()->{log.debug("Taks3....");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}},1,1,TimeUnit.SECONDS);
scheduleWithFixedDelay() 执行时间不会抵消delay时间
//result: 如果有sleep, scheduleWithFixedDelay的delay不会被第一次执行的时间抵消掉,而是在第二次开始重新计算。
//task4 第一次执行2S完后,再delay 1S,再次执行第二次。//16:04:06.244 [pool-1-thread-1] DEBUG TC50 - Taks4....//16:04:09.280 [pool-1-thread-1] DEBUG TC50 - Taks4....//16:04:12.302 [pool-1-thread-1] DEBUG TC50 - Taks4....//16:04:15.329 [pool-1-thread-1] DEBUG TC50 - Taks4....pool.scheduleWithFixedDelay(()->{log.debug("Taks4....");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}},1,1,TimeUnit.SECONDS);
 异常处理
  • 自己使用try/catch 捕获
  • 使用Callable +Future-->如果有异常就能使用get()拿到异常。

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

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

相关文章

用户画像——集群搭建

用户画像——集群搭建 1.设计一个规模合适的集群 1.1.1资源预估 1.1.2 选择服务器 2.部署和管理集群的工具 2.1 Hadoop的发展历程 2.2 部署和管理Hadoop的集群并不简单 三种工具的部署方式 3.自动创建虚拟机 3.1 什么是Vagrant&#xff1f; 3.2 安装Vagrant和概念介绍 3.3 使用…

c++——sort()函数

一、代码和效果 #include<bits/stdc.h> using namespace std;int main() {int a[6]{1,45,2,5,456,7};sort(a,a6);for(int i0; i<6; i){cout<<a[i]<<" "<<endl;}return 0; } 二、sort函数解析 &#xff08;从小到大&#xff09; std::so…

深入理解k8s kube-proxy

1、概述 我觉得只要大家知道kube-proxy是用来配置网络规则的而不是转发流量的&#xff0c;真正的流量由iptables/ipvs来转发就可以了。 网络是k8s的一个关键部分。理解k8s中网络组件如何工作可以帮助更好的设计和配置我们的应用。 kube-proxy就是K8s网络的核心组件。它把我们…

从“危”到“机”:HubSpot如何助企业转化出海营销CRM风险?

在全球化的大背景下&#xff0c;越来越多的企业选择出海拓展业务&#xff0c;以寻求更大的发展空间。然而&#xff0c;随着市场的扩大&#xff0c;企业在出海营销过程中也面临着各种风险。为了有效规避这些风险&#xff0c;许多企业选择借助HubSpot这样的专业营销软件。今天运营…

国际数字影像产业园构建成都文创产业园新地标!

国际数字影像产业园区&#xff0c;位于成都市金牛区的核心地带&#xff0c;不仅地理位置得天独厚&#xff0c;而且周边配套设施完善&#xff0c;交通便捷&#xff0c;不止成为北二环的新地标&#xff0c;正在构建成都文创产业园新地标&#xff01; 国际数字影像产业园这一片区…

专利年费缴纳后电子票据

专利年费缴纳后的电子票据 随着信息技术的快速发展&#xff0c;电子票据作为一种新型的支付与记录方式&#xff0c;已经深入到了我们日常生活的各个方面。特别是在知识产权领域&#xff0c;专利年费的缴纳也开始逐步实现电子化。 一、专利年费缴纳的重要性 专利年费是专利权…

5【PS让图片动起来】系列3-【时间轴 花瓣飘落】

【问题介绍】上章将花瓣拆分为一片片花瓣&#xff0c;现在让这片花瓣【动态】飘落吧~ PS选择“窗口”→ “时间轴” 下方弹出时间轴&#xff0c;其中01:00f就是这个位置为1秒的。中间的0 10f 20f等&#xff0c;为帧速率&#xff0c;也就是一秒的动画时间里有几帧的意思 下图…

Linux进阶篇:磁盘管理(二):LVM的创建、格式化和使用

Linux磁盘管理&#xff08;二&#xff09;&#xff1a;LVM的创建、格式化和使用 一、LVM原理回顾 LVM的工作原理进行一个总结&#xff1a; (1)物理磁盘被格式化为PV&#xff0c;空间被划分为一个个的PE (2)不同的PV加入到同一个VG中&#xff0c;不同PV的PE全部进入到了VG的PE…

2024年【R1快开门式压力容器操作】最新解析及R1快开门式压力容器操作试题及解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年【R1快开门式压力容器操作】最新解析及R1快开门式压力容器操作试题及解析&#xff0c;包含R1快开门式压力容器操作最新解析答案和解析及R1快开门式压力容器操作试题及解析练习。安全生产模拟考试一点通结合国家…

比特币革命:刚刚开始

作者&#xff1a;Marius Farashi Tasooji 编译&#xff1a;秦晋 要充分理解比特币及其含义&#xff0c;首先必须理解什么是价值&#xff0c;什么是货币。以及是什么赋予资产价值&#xff1f; 这个问题看似愚蠢&#xff0c;但实际上非常有趣。我们的生活是由我们消费或出售的物品…

scrapy 爬取m3u8视频

scrapy 爬取m3u8视频 【一】效果展示 爬取ts文件样式 合成的MP4文件 【二】分析m3u8文件路径 视频地址&#xff1a;[在线播放我独自升级 第03集 - 高清资源](https://www.physkan.com/ph/175552-8-3.html) 【1】找到m3u8文件 这里任务目标很明确 就是找m3u8文件 打开浏览器…

CPU和GPU分别是什么?主要区别是什么?

CPU与GPU的区别 定义性能特点应用领域结构与体积控制与协同工作特殊用途与限制脑图 定义 CPU: 中央处理器&#xff0c;负责解释并执行指令GPU: 图形处理器&#xff0c;专用于图形和视频处理 性能特点 CPU 计算量小&#xff0c;适合复杂运算结构组成复杂&#xff0c;控制逻辑…

老王讲IT:高级变量类型

IT老王&#xff1a;高级变量类型 目标 列表 元组 字典 字符串 公共方法 变量高级 知识点回顾 Python 中数据类型可以分为 数字型 和 非数字型 数字型 整型 (int) 浮点型&#xff08;float&#xff09; 布尔型&#xff08;bool&#xff09; 真 True 非 0 数 —— 非零…

一文了解ERC404协议

一、ERC404基础讲解 1、什么是ERC404协议 ERC404协议是一种实验性的、混合的ERC20/ERC721实现的&#xff0c;具有原生流动性和碎片化的协议。即该协议可让NFT像代币一样进行拆分交易。是一个图币的互换协议。具有原生流动性和碎片化的协议。 这意味着通过 ERC404 协议&#xf…

python--正则表达式-分组,贪婪模式,懒惰模式

分组&#xff1a; 分组在正则表达式中充当二次筛选 re.match(r"<[a-zA-Z].*>(.*)</[a-zA-Z]>", "<a>这个是百度的链接</a>") <re.Match object; span(0, 15), match<a>这个是百度的链接</a>> 贪婪与懒惰模式&am…

这个40M的小工具助你在windows下处理数据如虎添翼!!

这个40M的小工具助你在windows下处理数据如虎添翼&#xff01;&#xff01;7 使用windows系统时&#xff0c;如何获取一个大小为1M的文件的行数呢&#xff1f;一般是选择用excel或notepad等软件打开文件&#xff0c;然后查看最后一行的行号&#xff0c;那如果是想快速获取一个…

OSCP靶场--Nagoya

OSCP靶场–Nagoya 考点 1.nmap扫描 ## ┌──(root㉿kali)-[~/Desktop] └─# nmap 192.168.214.21 -sV -sC -Pn --min-rate 2500 -p- Starting Nmap 7.92 ( https://nmap.org ) at 2024-04-02 08:52 EDT Nmap scan report for 192.168.214.21 Host is up (0.38s latency).…

用Python实现输入点云索引绘制该点云法向量

import open3d as o3d# 读取pcd文件 pcd o3d.io.read_point_cloud(r"D:\PythonProjects\Codes\paper_images\back_point\voxel.pcd")# 计算法向量 pcd.estimate_normals(search_paramo3d.geometry.KDTreeSearchParamHybrid(radius0.1, max_nn30))# 选择要绘制法向量…

C#:用定时器监控定时器,实现中止定时器正在执行的任务,并重启

Windows服务中使用的比较多的是定时器&#xff0c;但这种定时任务有个比较大的毛病&#xff1a;有时会莫名其妙地停止执行&#xff08;长时间执行不完&#xff0c;假死&#xff09;&#xff0c;必须得手工重启Windows服务才能恢复正常。这个就太麻烦了。 有没有办法来实现定时…

MAC苹果电脑如何使用Homebrew安装iperf3

一、打开mac终端 找到这个终端打开 二、终端输入安装Homebrew命令 Homebrew官网地址&#xff1a;https://brew.sh/ 复制这个命令粘贴到mac的终端窗口&#xff0c;然后按回车键 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/in…