并发学习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.hqwc.cn/news/600802.html

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

相关文章

TS封装axios并约束请求参数以及响应的类型

封装一个简单的axios src/utils/axiosInstance.ts&#xff1a;其中定义了基本返回数据的类型 import axios, {AxiosInstance,AxiosResponse,AxiosError,AxiosRequestConfig, } from "axios"// 定义基本返回数据类型 export interface ApiResponse<T> {code: …

iOS:如何安全且优雅地操控数组元素

前言 在 iOS 开发的世界里&#xff0c;数组(Array)的操作频率高得令人咋舌。数组贯穿于我们每一个功能的实现和每一行代码的编写之中&#xff0c;一手托起了数据结构的半边天。但这位工具之王&#xff0c;有时候也会变身为导致程序崩溃的罪魁祸首。当访问越界&#xff0c;当插…

如何批量替换文件名中的字符?汇总3个超简单办法

如何批量替换文件名中的字符&#xff1f;在现代社会中&#xff0c;我们的生活充斥着大量的数字化文件&#xff0c;如照片、文档、音频和视频等。然而&#xff0c;有时这些文件的命名可能并不理想&#xff0c;包含了不必要的字符或格式。这时&#xff0c;批量替换文件名中的字符…

MySQL 主从 AUTO_INCREMENT 不一致问题分析

本文介绍了 MySQL5.7 中常见的replace into 操作造成的主从auto_increment不一致现象&#xff0c;一旦触发了主从切换&#xff0c;业务的正常插入操作会触发主键冲突的报错提示。 一、问题描述 1.1 问题现象 在 MySQL 5.7 版本中&#xff0c;REPLACE INTO 操作在表存在自增主键…

基于javassm实现的旅游景点线路网站

开发语言&#xff1a;Java 框架&#xff1a;ssm 技术&#xff1a;JSP JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.…

【CVE复现计划】CVE-2024-0195

CVE-2024-0195 简介&#xff1a; SpiderFlow是新一代开源爬虫平台&#xff0c;以图形化方式定义爬虫流程&#xff0c;不写代码即可完成爬虫。基于springbootlayui开发的前后端不分离,也可以进行二次开发。该系统/function/save接口存在RCE漏洞&#xff0c;攻击者可以构造恶意命…

【大功率汽车大灯升压方案】LED恒流驱动芯片FP7208升压车灯调光应用,PWM内部转模拟,调光深度1%,无频闪顾虑,低亮无抖动

宝马X5前中排座椅宽大舒适&#xff0c;车厢内储物空间丰富。操控性能极佳&#xff0c;底盘稳扎精良。原车为氙气灯&#xff0c;其实宝马的氙气大灯配的比其他车型要好&#xff0c;照明效果是没得说的。但是不管什么灯久了都会出现光衰的情况。下面这辆宝马X5车灯已老化严重。 宝…

Jmeter ServerAgent windows启动报错 NoClassDefFoundError

下载ServerAgent-2.2.3 后执行startAgent.bat 报错如下&#xff1a; 尝试解决方案一&#xff1a; 将整个ServerAgent-2.2.3文件夹复制到jdk目录下的bin目录下&#xff0c;然后重新进入目录执行startAgent.bat

MySQL如何创建存储过程

工作中有时候需要自己去创建存储过程&#xff0c;然后调用存储去获得一些数据等&#xff0c;接下来就给大家介绍下MySQL如何创建存储过程。 语法&#xff1a; CREATE PROCEDURE 存储程名([[IN|OUT|INOUT] 参数名 数据类型[,[IN|OUT|INOUT] 参数名 数据类型…]]) [特性 …] 过…

python 如何生成uuid

UUID&#xff08;Universally Unique Identifier&#xff09;是通用唯一识别码&#xff0c;在许多领域用作标识&#xff0c;比如我们常用的数据库也可以用它来作为主键&#xff0c;原理上它是可以对任何东西进行唯一的编码的。作为新手一看到类似varchar(40)这样的主键就觉得有…

西圣、漫步者、万魔开放式耳机怎么样?无广真实测评对比推荐

开放式耳机因其独特的音质体验和佩戴舒适度&#xff0c;受到了越来越多消费者的青睐。西圣、漫步者、万魔作为国内知名的耳机品牌&#xff0c;各自都推出了自家的开放式耳机产品&#xff0c;那么&#xff0c;这三款耳机究竟如何呢&#xff1f;身为开放式耳机党的我&#xff0c;…

python开发poc2,爆破脚本

#本课知识点和目的&#xff1a; ---协议模块使用&#xff0c;Request 爬虫技术&#xff0c;简易多线程技术&#xff0c;编码技术&#xff0c;Bypass 后门技术 下载ftp服务器模拟器 https://lcba.lanzouy.com/iAMePxl378h 随便创建一个账户&#xff0c;然后登录进去把ip改成…