一种线程安全的缓存工具实现方式

news/2025/1/22 0:48:05/文章来源:https://www.cnblogs.com/handsometaoa/p/18682740

前言

在多线程环境下,缓存是一个常见的性能优化手段。然而,实现一个线程安全的缓存并不容易,尤其是在高并发场景下,如何避免重复计算、保证数据一致性是一个挑战。
最近在读《Java并发编程实战》时,书中提到了一种基于 ConcurrentHashMap 和 FutureTask 的线程安全缓存实现方式,今天就来分享记录一下。

实现背景

在高并发场景中,缓存的核心作用是避免重复计算。比如,某个计算任务非常耗时,如果多个线程同时请求相同的数据,我们希望只计算一次,后续请求直接使用缓存结果。
然而,实现这样的缓存工具需要考虑以下几个问题:

线程安全:多个线程可能同时访问缓存,如何避免竞态条件?
避免重复计算:如何确保相同的计算任务只执行一次?
异常处理:如果计算任务抛出异常,如何清理缓存并通知调用方?

实现代码

public interface Computable<A, V> {V compute(A arg) throws InterruptedException;
}
import java.util.Map;
import java.util.concurrent.*;public class CacheUtils<A, V> implements Computable<A, V> {private final Map<A, Future<V>> cache = new ConcurrentHashMap<>();private final Computable<A, V> computable;public CacheUtils(Computable<A, V> computable) {this.computable = computable;}@Overridepublic V compute(A arg) throws InterruptedException {while (true) {Future<V> future = cache.get(arg);if (future == null) {Callable<V> eval = () -> computable.compute(arg);FutureTask<V> futureTask = new FutureTask<>(eval);future = cache.putIfAbsent(arg, futureTask);if (future == null) {future = futureTask;futureTask.run();}}try {return future.get();} catch (CancellationException e) {// 如果任务被取消,移除缓存中的 Futurecache.remove(arg, future);} catch (ExecutionException e) {// 如果计算任务抛出异常,移除缓存中的 Future 并抛出异常cache.remove(arg, future);throw new RuntimeException("Computation failed for argument: " + arg, e.getCause());}}}
}

实现思路说明

使用 ConcurrentHashMapputIfAbsent 方法来保证,同一时刻只能有一个线程可以将 FutureTask 放入缓存,从而避免相同任务的重复计算;当计算任务发生异常时,可以即时抛出错误/取消缓存结果。

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

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

相关文章

Solidity中数据的布局

1.Storage中 1.1-基本原则(基本的值类型) 在这里面的变量都是独立的,互相不影响,所以非常"安全"; 均存储在slot中,slot有2^256个,每个长度256位; 从0开始连续往后存储(除动态数组和映射),当有连续几个都小于256位时,会尝试将它们放在同一个slot中; 同时也…

深入探讨视图更新:提升数据库灵活性的关键技术

title: 深入探讨视图更新:提升数据库灵活性的关键技术 date: 2025/1/21 updated: 2025/1/21 author: cmdragon excerpt: 在现代数据库的管理中,视图作为一种高级的抽象机制,为数据的管理提供了多种便利。它不仅简化了复杂查询的过程,还能用来增强数据的安全性,限制用户对…

标准制修订信息管理系统:开启标准化管理的智能新时代

在当今快速发展的商业环境中,标准化工作对于企业的高效运营、质量提升以及行业竞争力的增强至关重要。然而,传统标准化管理方式往往面临着诸多痛点,如缺乏完善的动态管理体系、信息分散、查询与实施监督困难等。针对这些挑战,标准制修订信息管理系统应运而生,它以强大的技…

Catlike Coding Custom SRP笔记 - 平行光

原文链接:Directional Lights效果图 CustomRenderPipelineAsset.cs[CreateAssetMenu(menuName = "Rendering/Custom Render Pipeline")] public class CustomRenderPipelineAsset : RenderPipelineAsset {public bool useDynamicBatching = true; //启用动态合批pu…

uart串口的低速通信基础知识及模块代码(来自正点原子P15)

正点原子P15在PL端的uart电路参考,PS端uart和PL端一致,这里不做重复,uart电路由电脑端进行供电,即uart和主芯片之间除利用uart_tx和uart_rx通信外是独立的。从上图中可以看到,FPGA芯片的PL_UART1_TX连接到CH340的RXD管脚,FPGA芯片的PL_UART1_RX连接到 CH340 的 TXD 管脚,…

I/O框架

流的概念、流的分类、字节流、字符编码、字符流、打印流、转换流和File类。流的概念概念:内存与存储设备之间传输数据的通道。流的分类按方向【重点】输入流:将<存储设备>中的内容读入到<内存>中。 输出流:将<内存>中的内容写入到<存储设备>中。按单…

网站向顾客发送电子邮件

首先说一下,针对顾客未登录就可下单这个功能,为了使用户可以实时知晓货品的物流状态,使用了advance shipment tracking这个插件,这个插件不仅可以显示货品的物流信息,还可以在货品物流状态更新时向顾客发送电子邮件,这样就实现了顾客在未登录时就可以知道自己购买的商品的…

2025.1.20——1300

2025.1.20——1300A 1300 You are given a binary string \(s\). A binary string is a string consisting of characters 0 and/or 1. You can perform the following operation on \(s\) any number of times (even zero):choose an integer \(i\) such that \(1 \le i \le |…

制作docker 镜像上传到docker hub仓库

注册docker hub账号 https://hub.docker.com/ 参照此篇:https://www.cnblogs.com/yjlch1016/p/8998479.htmldocker hub上创建仓库https://hub.docker.com/repositories 本地制作镜像并上传在本地登陆 docker hub 帐号docker login将容器commit 成镜像,可以先用docker …

虚拟现实国标解读系列(一)帧率

大家好,我是ij(我的网名),中文名叫林志宏。 遵循我一贯的年底必摸鱼的习惯,我打算开始摸鱼来水一些文章,安慰下自己过去的一年。 有关注过我,听我吹过牛的都知道,我在几年前,也不知道几年前, 反正long long ago,我参与起草过一份牛逼的测试标准,国标GB/T 38258《虚…

【Linux网络】深入理解linux内核网络性能优化

一、网络请求优化 1.1 减少不必要的网络IO 在系统设计与开发过程中,应尽量避免不必要的网络I/O操作,尤其是在可以通过本地进程或内存内完成的场景下,避免使用网络通信来实现。网络虽然是现代分布式系统中的核心组件,能够连接不同模块、简化开发流程,并支持大规模系统的构建…

【Java开发】简化Maven项目依赖:优雅去除未使用Jar包

一、为什么要做这件事? 自从我踏入职场,便历经了技术革新的数次浪潮。从最初的.Net Framework、Winform、WPF,到Asp.Net MVC、Asp.Net MVC WebApi,再到Asp.Net Core 2.x的广泛应用,我始终深耕于.net领域。 然而,随着技术的不断演进,我逐渐发现.net相关的工作机会变得稀少…