Future.get() 的内存可见性保障

news/2025/3/25 14:55:46/文章来源:https://www.cnblogs.com/bestjosephine/p/18788559

Future.get() 的内存可见性保障

一、核心解释

在异步计算(通过 Future 提交的任务)中完成的所有操作(例如变量修改、数据写入等),在另一个线程调用 Future.get() 获取到结果后,这些操作的结果一定对当前线程可见。即:异步计算中的操作actions)会先于happen-before调用 get() 之后的操作,确保内存一致性。


二、关键概念拆解

1. happen-before 规则

  • 定义:Java 内存模型(JMM)中用于保证多线程环境下操作可见性和顺序性的规则。
  • 作用:若操作 A happen-before 操作 B,则 A 对内存的修改对 B 可见,且 A 的执行顺序在 B 之前

2. Future.get() 的内存语义

Future<T> future = executor.submit(task);
// ...其他操作...
T result = future.get(); // ✅ 此处建立 happen-before 关系
  • 触发条件:调用 future.get() 并成功返回结果时。
  • 内存效应
    任务线程中的所有操作(在 task 中执行)happen-before 主线程中 future.get() 之后的所有操作

三、实际意义与示例

1. 没有 happen-before 的典型问题

class ProblemExample {int count = 0; // 共享变量void unsafeDemo() {ExecutorService executor = Executors.newSingleThreadExecutor();executor.submit(() -> {count = 42; // 修改共享变量(可能对主线程不可见)});// ❌ 直接读取 count 值可能为 0(旧值)System.out.println(count); // 输出可能是 0 或 42(不确定)executor.shutdown();}
}
  • 问题:主线程可能看不到异步任务对 count 的修改(内存可见性问题)。

2. 通过 Future.get() 保证可见性

class SafeExample {int count = 0; // 共享变量void safeDemo() throws Exception {ExecutorService executor = Executors.newSingleThreadExecutor();Future<?> future = executor.submit(() -> {count = 42; // 修改共享变量});future.get(); // ✅ 建立 happen-before,确保修改可见System.out.println(count); // 必定输出 42executor.shutdown();}
}
  • 关键点
    future.get() 的返回建立了 happen-before 关系,确保主线程能看到 count = 42 的修改。

四、底层原理图解

sequenceDiagramparticipant TaskThread as 任务线程participant MainThread as 主线程participant Memory as 主内存TaskThread->>Memory: 写入数据(例如 count=42)Note over TaskThread: 1. 异步计算完成MainThread->>TaskThread: 调用 future.get()TaskThread-->>MainThread: 返回结果(建立 happen-before)MainThread->>Memory: 读取数据(必定看到 count=42)Note over MainThread: 2. get() 后的操作能看到所有修改

五、应用场景总结

场景 不使用 Future.get() 使用 Future.get()
共享变量可见性 需手动添加 volatile 或同步块 自动保证可见性
操作顺序性 需通过锁控制执行顺序 get() 前的操作必定先于后续操作
代码复杂度 高(需处理同步细节) 低(由 Future 机制保障)

六、扩展知识:其他建立 happen-before 的操作

操作 示例 作用
锁的释放与获取 synchronized 解锁操作 happen-before 后续加锁操作
volatile 变量写读 volatile int x; 写操作 happen-before 后续读操作
线程启动与结束 thread.start() / thread.join() 线程内操作 happen-before join() 后的操作

七、注意事项

  1. 仅适用于成功完成的 get() 调用
    若任务被取消或抛出异常,get() 会抛出异常,此时不保证内存可见性。

  2. 与显式同步结合使用

    Future<?> future = executor.submit(() -> {synchronized(lock) {count = 42; // 同步块内的修改}
    });
    future.get(); // 依然保证 happen-before
    
    • 此时既有 synchronized 的 happen-before,又有 future.get() 的保障(双重保证)。

通过 Future.get() 建立的 happen-before 关系,开发者无需手动处理内存同步细节,即可安全地在多线程环境下访问异步计算的结果。这是 Java 并发工具对开发者的重要保护机制。

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

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

相关文章

USTCPC 2025 游记

队名 合肥一中能不能多请点OI教练,二人队。队长 @包涵宇 ,省队爷。 Day -inf~0 随机写了几道有意思的 cf 。 bhy 又在做黑的插头 dp 。膜拜。 Day 1 早上被父母逼着学习文化课,结果作业做不完直接半红温状态,, 加上昨天做了 ~8h 文化课作业只完成了 1/2 ,然后直接不做了…

AI运维助手-LinuxAgent

介绍 LinuxAgent是基于LLM大模型的智能运维助手,通过接入DeepSeek API实现对Linux终端的自然语言控制,可以实现高效的运维工作。目前已经更新到2.0.5版本。 LinuxAgent能够理解用户的自然语言指令,只要描述需求,系统自动解析意图并执行,支持中文、英语等,可以理解复杂的多…

微服务Elasticsearch

Elasticsearch中倒排索引 为什么查询效率高 比如说一个商品 在一百万条数据中 你如果要搜的话 他会从一百万条数据中去寻找 比如说你要搜小米手机 但是倒排索引是这样的 //倒排 { "小米": [1], "手机": [1,2,3,4], } 他在给定的文档范围内去搜索 比如说正…

20242825 2024-2025-2 《网络攻防实践》第四周作业

@目录一. 实验内容1.1 实验内容概述1.2 实验相关知识概述二. 实验过程2.1 ARP缓存欺骗攻击2.1.1 实验环境配置2.1.2 连通性测试2.1.3 继续实验实验亮点出现问题检查问题解决问题2.2 ICMP重定向攻击2.2.1 实验环境配置2.2.2 连通性测试发现问题检查问题解决问题2.2.3 继续实验…

论文解读-Advances in 3D Generation: A Survey

论文介绍 题目: Advances in 3D Generation: A Survey 发表年份是 2024年,综述性质的文章,是看到腾讯发布了混元3D大模型所以来看看这个论文主要贡献 论文主要根据当前研究领域内不同的3D资产的生成方法进行了一个分类,将3D生成算法分为:前馈生成,基于优化的生成,基于过…

大数据技术

Hadoop Hadoop是一个能够对大量数据进行分布式处理的软件框架 HDFS ​ HDFS(Hadoop Distributed File System,Hadoop的分布式文件管理系统),是Hadoop的两大核心之一,用于管理数据和文件 Hadoop安装 ​ Hadoop可以在Window系统上运行,但其官方支持的操作系统只有Liunx,所以…

User\main.c(7): error: #5: cannot open source input file ds18b02.h: No such file or directory

报错截图解决途径 复制报错信息上网搜索,一般的解决办法:在c/c++选项中的Include Paths中包含头文件,将移植过来的代码放到指定的文件夹里,在Floder Setup中设置新移植的文件路径。很可惜我确认过我包含了头文件,但仍然报同样错误。 最后发现我代码里面是 #include"d…

Bean注入几种方式 (放入Spring容器)

目录 1、XML方式注入set方式注入构造方法注入2、注解方式注入@Component + @ComponentScan@Configuration + @Bean + @ComponentScan@Import3、实现ImportBeanDefinitionRegistrar接口 4、实现FactoryBean 5、实现BeanDefinitionRegistryPostProcessor 一、XML方式注入 在现在这…

自然资源数据要素支撑场景建设

自然资源数据要素与场景建设是推动经济社会高质量发展的重要途径。随着数字经济的快速发展,自然资源数据已成为关键的生产要素,其价值挖掘和利用成为推动经济社会发展的关键。自然资源数据要素的重要性自然资源数据要素包括地理、土地、矿产、海洋等多源数据,是支撑经济社会…

C语言分支与循环基础应用编程

实验任务1#include<stdio.h> #include<stdlib.h> #include<time.h> #define N 5int main() {int number;int i;srand(time(0));for(i=0;i<N;++i){number=rand()%100+1;printf("20490042%04d\n",number);}return 0; }问题1:生成一个1~100的随机…

NSSCTF Round#28 Team web题解

真是让人操心,但是又无法转移视线ez_ssrf 很简单的ssrf,知识点在网上搜都能搜到 payload http://node3.anna.nssctf.cn:28658@127.255.255.254/flag ez_php 第一部分有个非预期,直接/file就能出flag is_numeric绕过和md5强比较,很简单的知识点,这里就不细说了 第二部分网上…

Netty源码—4.客户端接入流程

大纲 1.关于Netty客户端连接接入问题整理 2.Reactor线程模型和服务端启动流程 3.Netty新连接接入的整体处理逻辑 4.新连接接入之检测新连接 5.新连接接入之创建NioSocketChannel 6.新连接接入之绑定NioEventLoop线程 7.新连接接入之注册Selector和注册读事件 8.注册Reactor线程…