Java类间变量共享与进度更新的实现策略

news2026/3/25 9:02:59
本文旨在探讨如何在Java中安全有效地共享和更新不同操作类别之间的变量值特别是在需要实时监控操作进度的场景中。我们将通过三种核心策略-观察者模式推动模型、轮询模式(拉模式)和基于多线程的共享状态管理——详细说明如何实现类间通信和数据同步并提供相应的代码示例和最佳实践建议。在Java应用程序开发中不同类别之间的数据交互是一种常见的需求。特别是在执行耗时的操作如文件复制和网络下载时我们通常需要在一个类别中执行任务并在另一个类别中实时显示或监控任务的进度。虽然通过静态变量直接访问是可行的但在多线程或复杂场景中可能会导致难以维护和调试的问题。本教程将讨论几个更强大、更专业的解决方案。1. 监控任务进度的挑战设想一个场景CopyFile 负责大文件的分块复制它将不断更新复制的数据量。而且 ProgressMonitor 此进度需要实时显示在用户界面或控制台上。核心挑战是CopyFile 如何通知更新后的进度值 ProgressMonitorProgressMonitor 如何获取到 CopyFile 最新进度如何在不紧密耦合两类的情况下实现这种通信如果 CopyFile 和 ProgressMonitor 在不同的线程中运行如何保证数据同步和线程安全下面我们将介绍三种主流实现策略。2. 策略1:观察者模式(推模型)观察者模式是一种行为设计模式它定义了对象之间的一对多依赖。当一个对象的状态发生变化时所有依赖它的对象都将被通知并自动更新。在这种模式下执行任务的类别Copy是“主题”Subject监控进度的类别Observer是“观察者”Observer。核心思想 Copy 类持有 Observer 每次进度更新时都会主动调用类实例 Observer 推动最新进度的方法。示例代码// Test.java - 主程序入口 public class Test { public static void main(String[] args) { // 创建观察者的例子 Observer observer new Observer(); // 创建复制任务的例子并将观察者注入 Copy copy new Copy(1000, observer); // 启动复制任务 copy.start(); } } // Observer.java - 进度观察者类 public class Observer { /** * 接收并显示进度更新 * param current 目前已完成的块数 * param total 总块数 */ public void updateProgress(int current, int total) { System.out.println(当前进度: current / total); } } // Copy.java - 文件复制任务类 public class Copy { public final int totalBlocks; // 总块数 private Observer observer; // 观察者实例 /** * 构造函数注入观察者 * param totalBlocks 文件总块数 * param observer 进度观察者 */ public Copy(int totalBlocks, Observer observer) { this.totalBlocks totalBlocks; this.observer observer; } /** * 复制启动文件模拟过程 */ public void start() { for (int current 1; current totalBlocks; current) { // 模拟耗时的操作 try { Thread.sleep(10); // 每次复制一小块数据 } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println(中断了复制过程。); return; } // 每次完成一个块通知观察者更新进度 observer.updateProgress(current, totalBlocks); } System.out.println(文件复制完成); return; } // 每次完成一个块通知观察者更新进度 observer.updateProgress(current, totalBlocks); } System.out.println(文件复制完成); } }优点实时性强 进度更新后立即通知观察者。职责分离 Copy 专注于复制逻辑Observer 专注于显示逻辑。低耦合 Copy 只需知道 Observer 有一个 updateProgress 方法不需要了解其内部实现。3. 策略二:轮询模式(拉模型)与观察者模式相反轮询模式不依赖于主动通知。在这种模式下监控进度的类别Observer会议定期主动执行任务类别Copy查询当前进度。核心思想 Observer 类持有 Copy 类的例子并在一个循环中不断调用 Copy 获得最新进展的方法。示例代码// Test.java - 主程序入口 public class Test { public static void main(String[] args) { // 创建复制任务的例子 Copy copy new Copy(1000); // 创建观察者实例并将复制任务注入 Observer observer new Observer(copy); // 启动观察者它将开始轮询进度 observer.start(); } } // Observer.java - 进度观察者类 public class Observer { private Copy copy; // 复制任务实例 /** * 构造函数注入复制任务 * param copy 复制任务实例 */ public Observer(Copy copy) { this.copy copy; } /** * 启动进度轮询 */ public void start() { System.out.println(开始监控文件复制进度...); while (copy.hasNextBlock()) { // 只要有未完成的块 // 模拟轮询间隔 try { Thread.sleep(100); // 每100毫秒查询一次进度 } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println(中断了进度监控。); return; } // 获取并显示当前进度 System.out.println(当前进度: copy.getCurrentBlock() / copy.totalBlocks); } System.out.println(文件复制完成(轮询确认)); } } // Copy.java - 文件复制任务类 public class Copy { public final int totalBlocks; // 总块数 private int currentBlock 0; // 目前已完成的块数 /** * 构造函数 * param totalBlocks 文件总块数 */ public Copy(int totalBlocks) { this.totalBlocks totalBlocks; // 在后台启动模拟复制过程的线程 new Thread(() - { for (int i 1; i totalBlocks; i) { try { Thread.sleep(20); // 模拟每次复制一小块数据的时间 } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println(中断了复制线程。); return; } currentBlock i; // 更新进度 } }).start(); } /** * 判断是否还有未完成的块 * return 若有未完成的块返回 true */ public boolean hasNextBlock() { return currentBlock totalBlocks; } /** * 获取目前已完成的块数 * return 目前已完成的块数 */ public int getCurrentBlock() { return currentBlock; } }优点控制权在观察者 观察者可以控制查询频率。实现相对简单 简单的状态共享不需要复杂的通知机制。缺点实时性稍差 根据轮询间隔进度更新可能会有延迟。资源消耗 频繁的轮询可能会导致不必要的CPU费用尤其是进度更新不频繁的时候。4. 策略3多线程共享状态与同步当 Copy 和 Observer 在不同的线程中运行时直接访问共享变量需要考虑线程安全。该策略结合了轮询的理念但更强调了多线程环境下的数据同步。核心思想 Copy 在单独的线程中执行任务并更新共享变量Observer 这个共享变量是在另一个线程中定期读取的。为了保证数据的可见性和一致性需要使用 volatile 关键字或更先进的同步机制。示例代码import java.util.concurrent.atomic.AtomicInteger; // Test.java - 主程序入口 public class Test { public static void main(String[] args) { // 创建共享进度对象 SharedProgress progress new SharedProgress(1000); // 创建并启动复制线程 Thread copyThread new Thread(new CopyTask(progress)); copyThread.setName(CopyThread); copyThread.start(); // 创建和启动观察者线程 Thread observerThread new Thread(new ProgressMonitor(progress)); observerThread.setName(ObserverThread); observerThread.start(); // 等待复制线程完成 try { copyThread.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println(主线程复制任务已完成。); } } // SharedProgress.java - 共享进度数据类 class SharedProgress { private final int totalBlocks; // 使用 AtomicInteger 保证原子操作或使用 volatile int currentBlock; // volatile 保证可见性但不能保证复合操作的原子性 private volatile int currentBlock 0; public SharedProgress(int totalBlocks) { this.totalBlocks totalBlocks; } public int getTotalBlocks() { return totalBlocks; } public int getCurrentBlock() { return currentBlock; } public void incrementProgress() { // 对于简单的自增操作volatile 你可以用适当的逻辑工作 // 但是如果需要更复杂的原子操作AtomicInteger 更安全 currentBlock; } public boolean isCompleted() { return currentBlock totalBlocks; } } // CopyTask.java - 拷贝任务在单独的线程中运行 class CopyTask implements Runnable { private SharedProgress progress; public CopyTask(SharedProgress progress) { this.progress progress; } Override public void run() { System.out.println(Thread.currentThread().getName() : 复制任务开始。); for (int i 0; i progress.getTotalBlocks(); i) { try { Thread.sleep(15); // 模拟复制每个数据的时间 } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println(Thread.currentThread().getName() : 中断了复制任务。); return; } progress.incrementProgress(); // 更新共享进度 } System.out.println(Thread.currentThread().getName() : 完成复制任务。); } } // ProgressMonitor.java - 进度监控器在单独的线程中运行 class ProgressMonitor implements Runnable { private SharedProgress progress; public ProgressMonitor(SharedProgress progress) { this.progress progress; } Override public void run() { System.out.println(Thread.currentThread().getName() : 进度监控开始。); while (!); while (!progress.isCompleted()) { try { Thread.sleep(80); // 每隔一段时间轮询进度进度 } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println(Thread.currentThread().getName() : 中断了进度监控。); return; } System.out.println(Thread.currentThread().getName() : 进度 progress.getCurrentBlock() / progress.getTotalBlocks()); } System.out.println(Thread.currentThread().getName() : 进度监控结束任务完成。); } }注意事项volatile 关键字 确保 currentBlock 变量的可见性。当线程修改时 currentBlock 当值时其他线程可以立即看到最新值而不是本地缓存的旧值。原子操作 如果 incrementProgress() 方法不仅仅是简单的 currentBlock但包含多个操作(例如) currentBlock currentBlock 1那么 volatile 不能保证其原子性。在这种情况下应该使用 java.util.concurrent.atomic 包中的类(如 AtomicInteger或 synchronized 关键字保护共享变量的访问。在上述示例中currentBlock 原子操作通常在JVM层面上进行但为了场景严谨复杂AtomicInteger 这是一种更安全的做法。线程管理 使用 Thread 类或 ExecutorService 管理线程生命周期。优雅退出 线程中断机制 (Thread.interrupt()) 建议停止线程而不是停止线程 Thread.stop()。5. 总结和最佳实践根据具体的应用场景和需求选择哪种策略观察者模式(推模型) 适用于需要实时和即时通知的场景生产者任务执行者希望主动通知消费者进度显示器。它提供了良好的解耦。轮询模式(拉模式) 适用于实时要求低或消费者想要控制获取频率的场景。实现相对简单但可能会有延迟和资源浪费。多线程共享状态 当任务和监控器必须在不同的线程中运行时这是一个不可避免的选择。通常需要正确处理线程安全性和数据可见性 volatile、synchronized 或 java.util.concurrent.atomic 包里的工具。通用建议解耦 尽量保持类之间的低耦合一个类不应过度依赖另一个类的内部实现细节。接口interface是实现解耦的有力工具。明确职责 每一类都要有明确单一的职责。错误处理 考虑任务中断、异常等情况并适当处理。并发考量 使用Java提供的并发工具和关键字始终优先考虑多线程环境中的线程安全。通过以上三种策略开发人员可以根据项目的具体需要灵活选择最合适的方式安全高效地分享Java中不同操作类别之间的变量和更新进度。

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

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

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

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…