当前位置: 首页 > news >正文

Java并发编程实战——结构化并发应用程序

文章目录

  • 6 任务执行
    • 6.1 在线程中执行任务
      • 6.1.1 串行地执行任务
      • 6.1.2 显式地为任务创建线程
      • 6.1.3 无限制创建线程的不足
    • 6.2 Executor框架
      • 6.2.1 示例:基于Executor的Web服务器
      • 6.2.2 执行策略
      • 6.2.3 线程池
      • 6.2.4 Executor的生命周期
      • 6.2.5 延迟任务与周期任务
    • 6.3 找出可利用的并行性
      • 6.3.1 示例:串行的页面渲染器
      • 6.3.2 携带结果的任务Callable和Future
      • 6.3.3 示例:使用Future实现页面渲染器
      • 6.3.4 在异构任务并行化中存在的局限
      • 6.3.5 CompletionService:Executor与BlockingQueue
      • 6.3.6 使用CompletionService实现页面渲染器
      • 6.3.7 为任务设置时间
      • 6.3.8 示例:旅行预订门户网站

6 任务执行

6.1 在线程中执行任务

应选择清晰的任务边界以及明确的任务执行策略。

一种自然的任务边界选择方式:以独立的客户请求为边界。

6.1.1 串行地执行任务

6.1.2 显式地为任务创建线程

程序6-1-2-1 在Web服务器中为每一个请求启动一个新的线程(不要这么做)

class ThreadPerTaskWebServer {public static void main(String[] args) throws IOException {ServerSocket socket = new ServerSocket(80);while (true) {final Socket  connection = socket.accept();Runnable task = new Runnable() {@Overridepublic void run() {handleRequest(connection);}};new Thread(task).start();}}
}

串行情况下可以提升性能。只要请求的到达速率不超出服务器的请求处理能力。

6.1.3 无限制创建线程的不足

线程生命周期的开销非常高。 线程创建需要时间,延迟处理的请求,并且需要JVM和操作系统提供一些辅助操作。
资源消耗。 活跃线程会消耗系统资源,尤其是内存。大量空闲的线程会占用许多内存,给垃圾收集器带来压力,而且大量线程在竞争CPU资源时还会产生其他的性能开销。如果有足够多的线程使所有的CPU保持忙碌状态,那么再创建更多的线程反而会降低性能。
稳定性。 在一定范围内,增加线程可以提高系统的吞吐率,但是如果超出了这个范围,再创建更多的线程会降低程序的执行速度。如果过多地创建一个线程,那么整个应用程序将崩溃。

6.2 Executor框架

Executor基于生产者-消费者模式,提交任务的操作相当于生产者(生成待完成的工作单元),执行任务的线程相当于消费者(执行完这些工作单元)。

6.2.1 示例:基于Executor的Web服务器

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;class TaskExecutionWebServer {private static final int NTHREADS = 100;private static final Executor exec = Executors.newFixedThreadPool(NTHREADS);public static void main(String[] args) throws IOException {ServerSocket socket = new ServerSocket(80);while (true) {final Socket connection = socket.accept();Runnable task = new Runnable() {@Overridepublic void run() {handleRequest(connection);}};exec.execute(task);}}
}

6.2.2 执行策略

使用线程池来取代new Thread方式。

6.2.3 线程池

  • newFixedThreadPool。固定长度的线程池。
  • newCachedThreadPool。如果线程池的当前规模超过了处理需求时,那么将回收空闲的线程;而当需求增加时,则可以添加新的线程,线程池的规模不存在任何限制。
  • newSingleThreadExecutor。创建单个工作线程来执行任务,如果这个线程异常结束,会创建另一个线程来替代。
  • newScheduledThreadPool。固定长度的线程池。以延迟或者定时的方式来执行任务。

6.2.4 Executor的生命周期

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;public class LifeCycleWebServer {private final ExecutorService exec = ...;public void start() throws IOException {ServerSocket socket = new ServerSocket(80);while (!exec.isShutdown()) {try {final Socket conn = socket.accept();exec.execute(new Runnable() {@Overridepublic void run() {handleRequest(conn);}});} catch (RejectedExecutionException e) {if (!exec.isShutdown()) {log("task submission rejected", e);}}}}public void stop() {exec.shutdown();}void handleRequest(Socket connection) {Request req = readRequest(connection);if (isShutdownRequest(req)) {stop();} else {dispatchRequest(req);}}
}

6.2.5 延迟任务与周期任务

推荐使用ScheduleThreadPoolExecutor。

Timer存在的问题:
(1)Timer支持基于绝对时间的调度机制,因此任务的执行对系统时钟变化很敏感。ScheduleThreadPoolExecutor只支持基于相对时间的变化。
(2)Timer执行所有的定时任务时只会创建一个线程。如果某个任务的执行时间过长,那么将破坏其他TimerTask的定时精确性。例如某个周期TimerTask需要每10ms执行一次,而另一个需要40ms,那么这个周期任务或者在40ms之后快速连续调用四次,或者彻底丢失4次调用。
(3)Timer线程并不捕获异常。如果TimerTask抛出异常终止了定时任务,timer不会回复线程执行,而是会错误的认为整个Timer都被取消了。 因此,已被调度但尚未执行的TimerTask将不会再执行,新的任务也不会被调度。

程序清单 6-2-5-1 错误的Timer行为


import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;public class OutOfTime {public static void main(String[] args) throws Exception {long startTime = System.currentTimeMillis();Timer timer = new Timer();timer.schedule(new ThrowTask(), 1);TimeUnit.SECONDS.sleep(1);long endTime = System.currentTimeMillis();System.out.println(endTime - startTime);timer.schedule(new ThrowTask(), 1);TimeUnit.SECONDS.sleep(5);}static class ThrowTask extends TimerTask {@Overridepublic void run() {throw new RuntimeException();}}
}Exception in thread "Timer-0" java.lang.RuntimeExceptionat executor.OutOfTime$ThrowTask.run(OutOfTime.java:22)at java.base/java.util.TimerThread.mainLoop(Timer.java:566)at java.base/java.util.TimerThread.run(Timer.java:516)
1002
Exception in thread "main" java.lang.IllegalStateException: Timer already cancelled.at java.base/java.util.Timer.sched(Timer.java:409)at java.base/java.util.Timer.schedule(Timer.java:205)at executor.OutOfTime.main(OutOfTime.java:15)

程序一秒钟就结束了,并抛出了异常。

在Java5.0或者更高的JDK中,将很少使用Timer。

6.3 找出可利用的并行性

6.3.1 示例:串行的页面渲染器

import java.util.ArrayList;
import java.util.List;public class SingleThreadRender {void renderPage(CharSequence source) {renderText(source);List<ImageData> imageData = new ArrayList<>();for (ImageInfo imageInfo : scanForImageInfo(source)) {imageData.add(imageInfo.downloadImage());}for (ImageData data : imageData) {renderImage(data);}}
}

6.3.2 携带结果的任务Callable和Future

6.3.3 示例:使用Future实现页面渲染器

程序清单6-3-3-1 使用Future等待图像下载

import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;public class FutureRender {private final ExecutorService executor = ...;void renderPage(CharSequence source) {final List<ImageInfo> imageInfos = scanForImageInfo(source);Callable<List<ImageData>> task = new Callable<List<ImageData>>() {@Overridepublic List<ImageData> call() throws Exception {List<ImageData> result = new ArrayList<>();for (ImageInfo imageInfo : imageInfos) {result.add(imageInfo.downloadImage());}return result;}};Future<List<ImageData>> future = executor.submit(task);renderText(source);try {List<ImageData> imageDataList = future.get();for (ImageData imageData : imageDataList) {renderImage(imageData);}} catch (InterruptedException e) {// 重新设置线程的中断状态Thread.currentThread().interrupt();// 由于不需要结果,因此取消任务future.cancel(true);} catch (ExecutionException e) {throw launcherThrowable(e.getCause());}}}

6.3.4 在异构任务并行化中存在的局限

只有大量相互独立且同构的任务可以并发进行处理时,才能体现出将程序的工作负载分配到多个任务中带来的真正性能提升。

6.3.5 CompletionService:Executor与BlockingQueue

6.3.6 使用CompletionService实现页面渲染器

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;public class Render {private final ExecutorService excutor;Render(ExecutorService excutor) {this.excutor = excutor;}void renderPage(CharSequence source) {List<ImageInfo> info = scanForImageInfo(source);CompletionService<ImageData> completionService = new ExecutorCompletionService<>(excutor);for (final ImageInfo imageInfo: info) {completionService.submit(new Callable<ImageData>() {@Overridepublic ImageData call() throws Exception {return imageInfo.downloadImage();}});}renderText(source);try {for (int t = 0, n = info.size(); t < n; t++) {Future<ImageData> f = completionService.take();ImageData imageData = f.get();renderImage(imageData);}} catch (InterruptedException e) {Thread.currentThread().interrupt();} catch (ExecutionException e) {throw launcherThrowable(e.getCause());}}
}

6.3.7 为任务设置时间

    Page renderPageWithAd() throws InterruptedException {long endNanos = System.nanoTime() + END_BUDGET;Future<Ad> f = excutor.submit(new FetchAdTask());// 在等待广告的同时显示页面。Page page = renderPageBody();Ad ad;try {long timeLeft = endNanos - System.nanoTime();ad = f.get(timeLeft, TimeUnit.NANOSECONDS);} catch (ExecutionException e) {ad = DEFAULT_AD;} catch (TimeoutException e) {ad = DEFAULT_AD;f.cancel(true);}page.setAd(ad);return page;}

6.3.8 示例:旅行预订门户网站

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;private class QuoteTask implements Callable<TravelQuote> {private final TravelCompany company;private final TravelInfo travelInfo;@Overridepublic TravelQuote call() throws Exception {return company.solicitQuote(travelInfo);}
}public List<TravelQuote> getRankedTravelQuotes(TravelInfo travelInfo, Set<TravelCompany> companies,Comparator<TravelQuote> ranking, long time, TimeUnit unit) throws InterruptedException {List<QuoteTask> tasks = new ArrayList<>();for (TravelCompany company : companies) {tasks.add(new QuoteTask(company, travelInfo));}List<Future<TravelQuote>> futures = exec.invokeAll(tasks, time, unit);List<TravelQuote> quotes = new ArrayList<>(futures.size());Iterator<QuoteTask> iterator = tasks.iterator();for (Future<TravelQuote> f : futures) {QuoteTask task = iterator.next();try {quotes.add(f.get());} catch (ExecutionException e) {quotes.add(task.getFailureQuote(e.getCause()));} catch (CancellationException e) {quotes.add(task.getTimeoutQuote(e.getCause()));}}Collections.sort(quotes, ranking);return quotes;
}

相关文章:

Java并发编程实战——结构化并发应用程序

文章目录 6 任务执行6.1 在线程中执行任务6.1.1 串行地执行任务6.1.2 显式地为任务创建线程6.1.3 无限制创建线程的不足 6.2 Executor框架6.2.1 示例&#xff1a;基于Executor的Web服务器6.2.2 执行策略6.2.3 线程池6.2.4 Executor的生命周期6.2.5 延迟任务与周期任务 6.3 找出…...

uniapp echarts 点击失效

这个问题网上搜了一堆&#xff0c;有的让你降版本&#xff0c;有的让你改源码。。。都不太符合预期&#xff0c;目前我的方法可以用最新的echarts。 这个方法就是由npm安装转为CDN&#xff0c;当然你可能会质疑用CDN这样会不稳定&#xff0c;那如果CDN的地址是本地呢&#xff1…...

手机开启应急预警通知 / 地震预警

前言 安卓手机在检测到地震时&#xff0c;将发送地震预警通知&#xff0c;但此设置是默认关闭的&#xff0c;原因是以防引发用户恐慌从而引发安全问题&#xff0c;且开启此设置需要完成指引教程&#xff0c;因此默认关闭此设置。下文介绍如何开启此设置。 开启方法 华为手机开…...

2020年12月 Python(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

一、单选题(共25题,每题2分,共50分) 第1题 执行语句print(10==10.0)的结果为? A:10 B:10.0 C:True D:False 正确的答案是 C:True。 解析:在Python中,比较运算符 “==” 用于比较两个值是否相等。在这个特定的比较中,整数10和浮点数10.0在数值上是相等的。…...

遇到无法复现的 Bug

当我们在软件开发过程中遇到无法复现的 Bug 时&#xff0c;这可能会让我们感到头疼和困惑。处理这种 Bug 需要一些技巧和方法来帮助我们更好地解决问题。本篇博客将为大家总结一些常用的技术手段和策略&#xff0c;希望能对开发者们在日常工作中遇到类似问题时提供一些帮助。 …...

虚拟列表的实现(简单易懂)

起因&#xff1a; app开发过程中遇到需要渲染3000行的列表&#xff0c;页面直接卡顿&#xff0c;所以开始研究起虚拟列表 实现前提条件&#xff1a; item项等高列表 实现思路&#xff1a; 首先是dom结构&#xff1a; 定义一个容器&#xff08;固定高度&#xff09;&#…...

【WordPress】如何在WordPress中实现真·页面路由

这篇文章也可以在我的博客中查看 页面路由 是什么 页面路由是指从url顺着网线砍到网站内容的途径&#xff0c;说人话就是地址与页面的映射。 就像真实世界的地址一样&#xff0c;我要找你&#xff0c;必须知道你的地址。 在网站中&#xff0c;通过地址找内容的机制&#xf…...

Android界面设计与用户体验

Android界面设计与用户体验 1. 引言 在如今竞争激烈的移动应用市场&#xff0c;提供优秀的用户体验成为了应用开发的关键要素。无论应用功能多么强大&#xff0c;如果用户界面设计不合理&#xff0c;用户体验不佳&#xff0c;很可能会导致用户流失。因此&#xff0c;在Androi…...

基于 FFmpeg 的跨平台视频播放器简明教程(八):音画同步

系列文章目录 基于 FFmpeg 的跨平台视频播放器简明教程&#xff08;一&#xff09;&#xff1a;FFMPEG Conan 环境集成基于 FFmpeg 的跨平台视频播放器简明教程&#xff08;二&#xff09;&#xff1a;基础知识和解封装&#xff08;demux&#xff09;基于 FFmpeg 的跨平台视频…...

【NLP pytorch】基于BiLSTM-CRF模型医疗数据实体识别实战(项目详解)

基于BiLSTM-CRF模型医疗数据实体识别实战 1数据来源与加载1.1 数据来源1.2 数据类别名称和定义1.3 数据介绍2 模型介绍2 数据预处理2.1 数据读取2.2 数据标注2.3 数据集划分2.4 词表和标签的生成3 Dataset和DataLoader3.1 Dataset3.2 DataLoader4 BiLSTM模型定义5 CRF模型6 模型…...

人工智能原理(1)

*请注意&#xff0c;本文仅供学习使用* 目录 一、人工智能发展 1、孕育期 2、摇篮期 3、形成期 4、发展期&#xff08;1970-1979&#xff09; 5、实用期 6、稳步发展期 二、何为人工智能 1、智能的主要观点 2、智能定义 3、人工智能定义 三、人工智能研究方法 1、…...

预测成真,国内传来三个消息,中国年轻人变了,创新力产品崛起

中国的年轻人真的变了&#xff01; 最近&#xff0c;国内传来三个消息&#xff0c;让外媒的预测成真。 第一&#xff0c;奥迪要开始用国产车的平台了。这里需要说明的是新能源汽车&#xff0c;奥迪也曾多次公开表示&#xff0c;承认了当前中国新能源汽车核心技术上的领先。 第…...

维深(Wellsenn):2023中国消费端VR内容开发商调研报告(附下载

关于报告的所有内容&#xff0c;公众【营销人星球】获取下载查看 核心观点 国内互联网大厂商入局VR&#xff0c;字节跳动、网易表态明确。字节跳动2021年收购国内头部VR硬件厂商PICO后&#xff0c;加速构建VR内容生态&#xff0c;2021年 成立海南创见未来当前已推出VR视频应用…...

redis事务管理详解

事务管理 事务管理乐观锁与悲观锁watch命令实现乐观锁watch命令示例 事务管理 Redis 提供了事务管理功能&#xff0c;可以通过 Redis 的 MULTI、EXEC、WATCH 和 DISCARD 命令来实现。 开启事务&#xff1a; 使用 MULTI 命令开始一个事务&#xff0c;表示接下来执行的命令都属于…...

国产低功耗蓝牙HS6621CxC/6621Px系列支持Find My网络功能方案芯片

目录 什么是“Find My“&#xff1f;HS6621系列简介 什么是“Find My“&#xff1f; “Find My”是苹果公司于19年前推出的针对失物追踪&#xff0c;Find My iPhone&#xff08;查找我的iPhone&#xff09;和Find My Friends&#xff08;查找朋友&#xff09;的结合体应用。为…...

【openGauss】分区表的介绍与使用

一、openGauss分区表介绍 在openGauss中&#xff0c;数据分区是在一个节点内部对数据按照用户指定的策略做进一步的水平分表&#xff0c;将表中的数据按照指定方式划分为多个互不重叠的部分。 对于大多数用户使用场景&#xff0c;分区表和普通表相比具有以下优点&#xff1a; …...

代码随想录算法训练营day57

文章目录 Day57回文子串题目思路代码 最长回文子序列题目思路代码 Day57 回文子串 647. 回文子串 - 力扣&#xff08;LeetCode&#xff09; 题目 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回文字符串 是正着读和倒过来读一样的字符串。…...

【基础类】—前后端通信类系统性学习

一、什么是同源策略及限制 同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。源&#xff1a;协议、域名和端口&#xff0c; 默认端口是80 三者有一个不同&#xff0c;即源不同&#xff0c;就是跨域 ht…...

vite项目中使用@代表根路径

1.配置vite.config.ts import { defineConfig } from vite import vue from vitejs/plugin-vue import path from pathexport default defineConfig({plugins: [vue()],resolve: {alias:{: path.resolve(__dirname, src) }} })2.报错path和__dirname 找不到模块“path”或其相…...

冶金化工操作VR虚拟仿真实验软件提高员工们协同作业的配合度

对于高风险行业来说&#xff0c;开展安全教育培训是企业的重点工作&#xff0c;传统培训逐渐跟不上时代变化和工人需求&#xff0c;冶金安全VR模拟仿真培训系统作为一种新型的教育和培训工具&#xff0c;借助VR虚拟现实技术为冶金行业的工人提供一个安全、高效的培训环境。 冶金…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

腾讯云V3签名

想要接入腾讯云的Api&#xff0c;必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口&#xff0c;但总是卡在签名这一步&#xff0c;最后放弃选择SDK&#xff0c;这次终于自己代码实现。 可能腾讯云翻新了接口文档&#xff0c;现在阅读起来&#xff0c;清晰了很多&…...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值&#xff0c;最大值左侧的数值严格单调递增&#xff0c;最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值&#xff0c;最小值左侧的数值严格单调递减&#xff0c;最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

jdbc查询mysql数据库时,出现id顺序错误的情况

我在repository中的查询语句如下所示&#xff0c;即传入一个List<intager>的数据&#xff0c;返回这些id的问题列表。但是由于数据库查询时ID列表的顺序与预期不一致&#xff0c;会导致返回的id是从小到大排列的&#xff0c;但我不希望这样。 Query("SELECT NEW com…...