避免在ConcurrentHashMap.computeIfAbsent()中进行递归

有时我们会提供糟糕的建议。 就像该文章中有关如何将Java 8用于缓存的功能性方法来计算斐波那契数的文章一样 。 正如我们的读者之一马蒂亚斯(Matthias)在评论中注意到的那样 ,提出的算法可能永远不会停止。 考虑以下程序:

public class Test {static Map<Integer, Integer> cache = new ConcurrentHashMap<>();public static void main(String[] args) {System.out.println("f(" + 25 + ") = " + fibonacci(25));}static int fibonacci(int i) {if (i == 0)return i;if (i == 1)return 1;return cache.computeIfAbsent(i, (key) -> {System.out.println("Slow calculation of " + key);return fibonacci(i - 2) + fibonacci(i - 1);});}
}

它将至少在以下Java版本上无限期运行:

C:\Users\Lukas>java -version
java version "1.8.0_40-ea"
Java(TM) SE Runtime Environment (build 1.8.0_40-ea-b23)
Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)

这当然是“功能”ConcurrentHashMap.computeIfAbsent() Javadoc读取:

如果指定的键尚未与某个值关联,则尝试使用给定的映射函数计算其值,除非为null,否则将其输入此映射。 整个方法调用是原子执行的,因此每个键最多可应用一次该功能。 在进行计算时,可能会阻止其他线程对此映射进行的某些尝试的更新操作,因此计算应简短而简单, 并且不得尝试更新此映射的任何其他映射

尽管并非出于相同的并发原因,“不得”的措辞是明确的合同,我的算法违反了该合同。

Javadoc还读取:

抛出:

IllegalStateException-如果计算可检测到尝试对此地图进行递归更新,否则将永远无法完成

但是不会抛出该异常。 也没有任何ConcurrentModificationException。 相反,该程序永远不会停止。

解决此具体问题的最简单的使用现场解决方案是不使用ConcurrentHashMap,而仅使用HashMap:

static Map<Integer, Integer> cache = new HashMap<>();

覆盖超类型合约的子类型

Map.computeIfAbsent() HashMap.computeIfAbsent()Map.computeIfAbsent() Javadoc禁止这种递归计算,这当然是荒谬的,因为缓存的类型是Map<Integer, Integer> ,而不是ConcurrentHashMap<Integer, Integer> 。 子类型彻底重新定义超级类型协定是非常危险的( Set vs. SortedSet是问候)。 因此,在超级类型中也应禁止执行此类递归。

进一步参考

尽管合同问题只是人们的看法,但停顿问题显然是一个漏洞。 我还在Stack Overflow上记录了此问题,在该问题中 , Ben Manes提供了一个有趣的答案,导致了先前的错误报告(截至2015年初尚未解决):

  • https://bugs.openjdk.java.net/browse/JDK-8062841

我自己的报告(可能是上述报告的副本)也很快被接受,原因是:

  • https://bugs.openjdk.java.net/browse/JDK-8074374

Oracle正在研究此问题时,请记住:

切勿在ConcurrentHashMap.computeIfAbsent()方法内部进行递归。 如果您正在实现集合,并且认为编写一个可能无限的循环是个好主意,请再考虑一下,然后阅读我们的文章:

无限循环。 或者:可能出错的任何东西都可以 )

墨菲总是对的。

翻译自: https://www.javacodegeeks.com/2015/03/avoid-recursion-in-concurrenthashmap-computeifabsent.html

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

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

相关文章

java调用wvsc.exe_c语言 函数的调用方法

欢迎加入编程爱好者 QQ群 群号 57616770中都只有一个主函数main()&#xff0c;但实用程序往往由多个函数组成。函数是&#xff23;源程序的基本模块&#xff0c;通过对函数模块的调在第一章中已经介绍过&#xff0c;&#xff23;源程序是由函数组成的。虽然在前面各章的程序用实…

ms2005 SQL Server设置改为SQL Server身份验证

1.为 SQL Server 2005 Express Edition 或 SQL Server 2005 Developer Edition 启用远程连接 必须为要从远程计算机连接到的每个 SQL Server 2005 实例启用远程连接。为此&#xff0c;请按照下列步骤操作&#xff1a; 1.单击“开始”&#xff0c;依次指向“程序”、“Microsoft…

JPA和Hibernate级联类型的初学者指南

介绍 JPA将实体状态转换转换为数据库DML语句。 由于对实体图进行操作很常见&#xff0c;因此JPA允许我们将实体状态更改从父级传播到子级 。 通过CascadeType映射配置此行为。 JPA与Hibernate级联类型 Hibernate支持所有JPA级联类型和一些其他旧式级联样式。 下表绘制了JPA级…

EE JSP:使用JSTL标记库生成动态内容

除了在JSP中编写自己的定制标记之外&#xff0c;您还将发现Java EE实际上提供了一组Java标准标记库&#xff08;JSTL&#xff09;供您使用。 这些内置标签包括重复&#xff08;for-loop&#xff09;标签&#xff0c;条件标签&#xff0c;变量声明和输出标签等。库还带有许多实用…

Sublime text 2下alignment插件无效的解决办法

在sublime text 2中安装了alignment插件&#xff0c;但使用快捷键‘ctrlalta无效&#xff0c;经过各种方法依然无效&#xff0c;最后找到了这个“Doesnt work at all for me (full steps)”&#xff0c;方法就是用sb打开%sb 路径%\Data\Packages\Alignment目录下的alignment.py…

php入口函数,php 常用的系统函数

字符串函数strlen&#xff1a;获取字符串长度&#xff0c;字节长度substr&#xff1a;字符串截取&#xff0c;获取字符串(按照字节进行截取)strchr&#xff1a;与substr相似&#xff0c;从指定位置截取一直到最后strrchr(获取文件后缀名)&#xff1a;与strchr一样&#xff0c;只…

startActivityForResult的使用和用法

startActivityForResult的使用和用法 startActivityForResult 和 onActivityResult在activity间传递数据AndroidManifest.xml<applicationandroid:icon"drawable/ic_launcher"android:label"string/app_name" ><activityandroid:name".KakuL…

如何使用Java泛型映射不同的值类型

有时&#xff0c;一般的开发人员会遇到这样的情况&#xff0c;即他必须在特定容器内映射任意类型的值。 但是&#xff0c;Java集合API仅提供与容器相关的参数化。 例如&#xff0c;这将HashMap的类型安全使用限制为单个值类型。 但是&#xff0c;如果您想混合苹果和梨怎么办&am…

php用正则去掉一些固定字符,用PHP正则表达式清除字符串的空白

我们经常会处理来自用户输入或从数据库中读取的数据&#xff0c;可能在你的字符串中有多余的空白或制表符&#xff0c;回车等。存储这些额外的字符是有点浪费空间的。如果您想要去掉字符串开始和结束的空白可以使用PHP内部函数trim() 。但是, 我们经常想完全清除空白。需要把开…

Windows上编译libpng

Windows上编译libpng 下载libpng 1.5.10并解压到[工作目录]/png/libpng-1.5.10 用CMake选择png/libpng-1.5.10目录并Configure&#xff1a; CMAKE_C_FLAGS_DEBUG/D_DEBUG /MTd /Zi /Ob0 /Od /RTC1 CMAKE_C_FLAGS_RELEASE/MT /O2 /Ob2 /D NDEBUG CMAKE_INSTALL_PREFIX[工作目录…

在Graphite中存储Hystrix的几个月历史指标

Hystrix的杀手级功能之一是低延迟&#xff0c;数据密集型且美观的仪表板 &#xff1a; 即使这只是Hystrix实际操作的副作用&#xff08;断路器&#xff0c;线程池&#xff0c;超时等&#xff09;&#xff0c;它也往往是最令人印象深刻的功能。 为了使其工作&#xff0c;您必须…

html和php文件怎么连接,html页面跟php文件连接的方法

html页面跟php文件连接的方法发布时间&#xff1a;2020-09-25 11:11:05来源&#xff1a;亿速云阅读&#xff1a;115作者&#xff1a;小新小编给大家分享一下html页面跟php文件连接的方法&#xff0c;相信大部分人都还不怎么了解&#xff0c;因此分享这篇文章给大家参考一下&…

java定义一个course类,求指教定义一个学生类 ,大学生小学生,定义一个选课接口...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼interface XC{abstract String CourseName();abstract String CourseID();}abstract class Student{protected String Name;protected String Ban;protected String Sex;public Student(){}public Student(String Name,String Ban,…

NYOJ-----最少乘法次数

最少乘法次数 时间限制&#xff1a;1000 ms | 内存限制&#xff1a;65535 KB难度&#xff1a;3描述给你一个非零整数&#xff0c;让你求这个数的n次方&#xff0c;每次相乘的结果可以在后面使用&#xff0c;求至少需要多少次乘。如24&#xff1a;2*222&#xff08;第一次乘&a…

在Java 7或更早版本中使用Java 8 Lambda表达式

我认为没有人会拒绝Java 8引入的Lambda表达式的有用性。但是&#xff0c;许多项目都停留在Java 7甚至旧版本上。 升级可能既耗时又昂贵。 如果第三方组件与Java 8不兼容&#xff0c;则可能根本无法升级。 除此之外&#xff0c;整个Android平台都停留在Java 6和7上。 尽管如此…

php获得昨天零时的时间戳,php 获取时间今天明天昨天时间戳

echo "今天:".date("Y-m-d")."";echo "昨天:".date("Y-m-d",strtotime("-1 day")), "";echo "明天:".date("Y-m-d",strtotime("1 day")). "";echo "一周…

Zend Debugger 配置

到官网 http://www.zend.com/en/products/studio/downloads 下载 windows 版 Studio Web Debugger打开下载得到的压缩包&#xff0c;里面有一些文件夹列表&#xff08;4_3_x_comp &#xff0c; 4_4_x_comp &#xff0c; 5_0_x_comp &#xff0c; 5_2_x_comp &#xff0c; 5_2_x…

JavaFX技巧17:带有AnchorPane的动画工作台布局

最近&#xff0c;我不得不为应用程序实现一个布局&#xff0c;其中可以根据用户是否登录来隐藏或通过滑入/滑出动画显示或显示菜单区域和状态区域。 以下视频显示了实际的布局&#xff1a; 在过去&#xff0c;我可能会使用自定义控件和自定义布局代码来实现这种行为&#xff0…

php投票系统中各个文件的作用说明,PHP开发简单投票系统之投票页面功能模块(二)...

当完成前面的投票后&#xff0c;可以选择点击查看结果查看每个项目的总票数和所有项目的投票百分比。点击“查看结果”后程序会自动计算每个项目的票数和所占百分比。使用了隐藏表单属性隐藏域在页面中对于用户是不可见的&#xff0c;在表单中插入隐藏域的目的在于收集或发送信…

使用Spring Boot对REST URL进行集成测试

我们正在构建一个具有REST接口的Spring Boot应用程序&#xff0c;并且在某个时候我们想测试我们的REST接口&#xff0c;并在可能的情况下将此测试与常规单元测试集成。 一种方法是Autowire我们的REST控制器&#xff0c;并使用它来调用我们的端点。 但是&#xff0c;这不会完全融…