Java Language Architect at Oracle,开发 Java 语言的,就问你怕不怕。
同时,他还是我多次推荐过的《Java并发编程实践》这本书的作者。
好了,现在也找到大佬背书了,接下来带你看看高赞回答是怎么说的。
前部分就不详说了,其实就是我们前面提到的那一些点,不能用 Integer ,涉及到缓存内、缓存外巴拉巴拉的...
关注划线的部分,我加上自己的理解给你翻译一下:
如果你真的必须用 Integer 作为锁,那么你需要搞一个 Map 或 Integer 的 Set,通过集合类做映射,你就可以保证映射出来的是你想要的明确的一个实例。而这个实例,就那可以拿来做锁。
然后他给出了这样的代码片段:
就是用 ConcurrentHashMap 然后用 putIfAbsent 方法来做一个映射。
比如多次调用 locks.putIfAbsent(200, 200),在 map 里面也只有一个值为 200 的 Integer 对象,这是 map 的特性保证的,无需过多解释。
但是这个哥们很好,为了防止有人转不过这个弯,他又给大家解释了一下。
首先,他说你也可以这样的写:
但这样一来,你就会多产生一个很小成本,就是每次访问的时候,如果这个值没有被映射,你都会创建一个 Object 对象。
为了避免这一点,他只是把整数本身保存在 Map 中。这样做的目的是什么?这与直接使用整数本身有什么不同呢?
他是这样解释的,其实就是我前面说的“这是 map 的特性保证的”:
当你从 Map 中执行 get() 时,会用到 equals() 方法比较键值。
两个相同值的不同 Integer 实例,调用 equals() 方法是会判定为相同的 。
因此,你可以传递任何数量的 "new Integer(5)" 的不同 Integer 实例作为 getCacheSyncObject 的参数,但是你将永远只能得到传递进来的包含该值的第一个实例。
就是这个意思:
汇总一句话:就是通过 Map 做了映射,不管你 new 多少个 Integer 出来,这多个 Integer 都会被映射为同一个 Integer,从而保证即使超出 Integer 缓存范围时,也只有一把锁。
除了高赞回答之外,还有两个回答我也想说一下。
第一个是这个:
免费送你一个英语小知识,不用客气。
第二个应该关注的回答排在最后:
这个哥们叫你看看《Java并发编程实战》的第 5.6 节的内容,里面有你要寻找的答案。
巧了,我手边就有这本书,于是我翻开看了一眼。
第 5.6 节的名称叫做“构建高效且可伸缩的结果缓存”
好家伙,我仔细一看这一节,发现这是宝贝呀。
你看书里面的示例代码:
都是从缓存中获取,拿不到再去构建。
不同的地方在于书上把 synchronize 加在了方法上。但是书上也说了,这是最差的解决方案,只是为了引出问题。
随后他借助了 ConcurrentHashMap、putIfAbsent 和 FutureTask 给出了一个相对较好的解决方案。
你可以看到完全是从另外一个角度去解决问题的,根本就没有在 synchronize 上纠缠,直接第二个方法就拿掉了 synchronize。
看完书上的方案后我才恍然大悟:好家伙,虽然前面给出的方案可以解决这个问题,但是总感觉怪怪的,又说不出来哪里怪。原来是死盯着 synchronize 不放,思路一开始就没打开啊。
书里面一共给出了四段代码,解决方案层层递进,具体是怎么写的,由于书上已经写的很清楚了,我就不赘述了,大家去翻翻书就行了。
没有书的直接在网上搜“构建高效且可伸缩的结果缓存”也能搜出原文。
我就指个路,看去吧。
本文已收录至个人博客,欢迎来玩: