Java:返回对象的副本

时间:2010-10-19 06:31:49

标签: java

在下面的代码片段中,为什么需要返回data [i]的副本。如果没有复制,多线程环境中究竟会发生什么。

protected Object[] data; 
..
public synchronized Object get(int i) 
throws NoSuchElementException 
{   if (i < 0 || i >= size ) 
      throw new NoSuchElementException();
    return data[i]; 
}

7 个答案:

答案 0 :(得分:4)

  

为什么需要返回数据副本[i]。

您将在索引i处返回引用的副本,而不是该对象的副本。

除非您通过例如data[i].clone()创建对象的副本,否则您将始终拥有一个对象并在您的线程中共享对它的引用。在多个线程中共享对单个对象的引用没有任何问题。

  

如果没有复制,多线程环境会发生什么。

好吧,除非你使用synchronized方法同步你的线程,否则等待/通知或java.util.concurrent - 类可能会以race-conditions结束。竞争条件基本上是执行结果取决于特定调度(线程可以执行的顺序)的情况。

如果您在线程之间共享某个类的对象,则应将其设计为"thread safe"。如果您的对象代表value object,我建议您将其设为immutable

答案 1 :(得分:2)

嗯,我觉得这个方法没有任何问题 - 它没有制作副本。这取决于你打算在之后对返回的对象做什么。如果您要从不同的线程修改它,那么最好为每个线程返回一个副本。

答案 2 :(得分:1)

由于该方法是同步的,并且除非同一个包中没有其他人操作数据数组,否则应该没有多线程问题。

答案 3 :(得分:1)

因为调用代码可能更舒适。如果你没有返回副本(如你的例子所示),你必须同步返回的引用(data [i]),如果你想避免竞争条件。

答案 4 :(得分:1)

  

希望返回数据副本[i]

由于你的get()是同步的,因此很多线程可能会并行访问这些对象。如果以下几点真实,这将导致问题

  • 存储在数组中的对象是可静音的(可以改变状态)
  • 对象方法未同步(对象不是线程安全的)

如果这些都是真的,如果两个线程同时操作对象,则最终可能会出现处于无效状态的对象。创建对象的副本可以防止这种情况,因为每个线程都将处理自己的副本。

答案 5 :(得分:0)

如果多个线程可以直接访问同一个对象,则可能会出现意外行为问题。

请考虑以下事项:

主题A:

Object foo = get(1);
foo.member += 5;

主题B:

Object bar = get(1);
bar.member = 2;

假设这两个线程同时运行,您无法知道data [1]中存储的对象的最终状态。线程切换控制的点是不可预测的,虽然99%的时间你可能没问题,但是你的代码可能会出现间歇性错误。

您真正想要做的是使用“synchronized”关键字保护任何修改对象data []状态的方法。吸气剂,制定者等都应该同步。拥有一个只传递对象引用的同步方法是没有意义的。

答案 6 :(得分:0)

请不要使用clone来创建副本,它有自己的错误。使用复制构造函数或创建一个新对象并设置成员值并返回相同的值。