&引用;实践中的Java并发性";-缓存的线程安全数字分解器(清单2.8)
在以下代码中(复制自第2章第2.5节清单2.8): 为什么要克隆&引用;实践中的Java并发性";-缓存的线程安全数字分解器(清单2.8),java,thread-safety,clone,local-variables,Java,Thread Safety,Clone,Local Variables,在以下代码中(复制自第2章第2.5节清单2.8): 为什么要克隆因子,最后因子数组?它不能简单地写为factors=lastFactors和lastFactors=系数?仅仅因为因子是一个局部变量,然后它被传递给编码响应,后者可以修改它 希望问题清楚。谢谢。基本猜测答案:如果您计划修改对象,并且不想修改原始对象,则需要克隆,在您的情况下,factors=lastFactors.clone()之所以完成,是因为您不想修改lastFactors,而是要克隆它并将其发送到encodeIntoRespo
因子
,最后因子
数组?它不能简单地写为factors=lastFactors代码>和lastFactors=系数代码>?仅仅因为因子
是一个局部变量,然后它被传递给编码响应
,后者可以修改它
希望问题清楚。谢谢。基本猜测答案:如果您计划修改对象,并且不想修改原始对象,则需要克隆,在您的情况下,factors=lastFactors.clone()代码>之所以完成,是因为您不想修改lastFactors
,而是要克隆它并将其发送到encodeIntoResponse(resp,factors)代码>可能包含修改代码。这称为防御性复制。数组和其他数组一样都是对象,所以
factors = lastFactors
将lastFactos的引用视为factors,反之亦然。所以任何人都可以覆盖你无法控制的状态。例如:
private void filterAndRemove(BigInteger[] arr);
private void encodeIntoResponse(..., BigInteger[] factors) {
filterAndRemove(factors);
}
根据我们的理论赋值,filterAndRemove也会影响原始的lastFactorials。克隆数组的唯一原因是阻止(在这种情况下是并发的)修改数组元素。但是,假设没有其他方法修改lastFactors
引用的数组,那么在这种情况下,这看起来是不可能的,这在给定示例时是有意义的。存储在factors
和lastFactors
中的数组都是由factor
以完整状态创建和返回的,它们的引用在同步块中分配,这将使它们安全发布
除非encodeIntoResponse
修改其因子
参数的元素,否则在我看来,对克隆
的调用是不必要的。我同意书的作者本可以更好地解释这一部分
确实,为了正确地实现线程安全,您必须使用同一个锁同步读取和写入操作;在上面的代码中,为了最小化同步量,作者决定在不进行同步的情况下执行encodeintoress(…)
:因为encodeintoress(…)
method读取factors
引用的数组内容,然后作者将其克隆到一个新数组中
注意:虽然因子
确实是一个局部变量,但一般来说,它仍然需要被克隆,因为同一数组是由同步和不同步的代码读取的,如果我们将引用(不克隆)传递给lastFactors
和encodeIntoResponse(…)
,就会发生这种情况
但是,正如@khachik在问题中和@david harkness在回答中正确指出的那样,在这种特定情况下,clone
调用是不必要的,因为lastFactors
是安全发布的,并且在发布后不会被修改。如果您更改factors=lastFactors.clone()代码>到<代码>因子=最后一个因子
,两个因子
和lastFactors
都指向同一个对象,因子
不再是一个局部变量,它变成了一个共享可变状态
假设有三个请求,请求A、B、C。请求A和B发送的数字是10,但请求C发送的数字是20。如果发生以下执行顺序,并且您更改factors=lastFactors.clone()代码>到<代码>因子=最后一个因子代码>
servlet服务器接收到请求A,整个服务
方法被执行,现在lastNumber
是10
,lastFactors
是[1,2,5,10]
servlet服务器同时接收请求B和C,首先处理请求B,但在退出第一个已同步的块后(现在对于请求B,因子是[1,2,5,10]
,这是正确的),处理请求C
对于请求C,执行整个服务方法,它将lastFactors
从[1,2,5,10]
更改为[1,2,4,5,10,20]
,因为这两个因素lastFactors
指向同一个对象,因素现在也是[1,2,4,5,10,20]/code>请求B的响应应该是[1,2,5,10]
,但现在是[1,2,4,5,10,20]
非常感谢。如果我错了,请纠正我,但是没有代码修改lastFactor
或factor
中保存的数组。因此,lastFactors
中保存的数组不能同时修改。需要同步才能将分配给lastNumber
,lastFactors
原子,但不能阻止同时修改lastFactors
。所以这个问题(对我)是开放的。。。在对代码进行更仔细的审查并重新阅读@khachik评论后,我认为@david harkness的上述回答是最正确的:因为lastFactors
是安全发布的,并且在它发布后没有被修改。看起来克隆它是不必要的。经过这么多年,你得到了这个问题的答案吗?在3中,当你做lastFactors=factors时,它只会修改lastFactor引用,而不会修改另一个线程中的factors引用。它们都指向同一个对象,现在lastFactors指向一个新对象
private void filterAndRemove(BigInteger[] arr);
private void encodeIntoResponse(..., BigInteger[] factors) {
filterAndRemove(factors);
}