&引用;实践中的Java并发性";-缓存的线程安全数字分解器(清单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

在以下代码中(复制自第2章第2.5节清单2.8):

为什么要克隆
因子
最后因子
数组?它不能简单地写为
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);
    }