0

假设我有以下类,其中包含ElementClass对象列表并且ElementClass是可变的:

public class ListContainer {
    private List<ElementClass> elements = new ArrayList<ElementClass>();

    public ListContainer(int n) {
        for (int i = 0; i < n; ++i)
            elements.add(new ElementClass());
    }

    public ElementClass getElement(String index) {
        int i = Integer.parseInt(index);
        if (i < elements.size())
            return elements.get(i);
        return null;
    }
}

如何确保ElementClass两个不同的线程不会获取对对象的相同引用并冒着意外行为的风险?这可以通过更改ListContainer班级来完成,还是必须在班级中采取所有线程安全措施ElementClass

4

2 回答 2

1

首先,您的类(如编写的)实际上是不可变的(从 API 角度来看),因为一旦创建列表,就没有允许修改列表的操作。

(您可能可以对列表中的实例执行变异操作ElementClass。但这不会改变列表本身。)

如果您确实向 中添加了变异操作ListContainer,则需要做一些事情来使它们成为线程安全的。但是假设内部List对象没有“泄漏”,使相关ListContainer操作同步就足够了。


实际上,所写的类并不是 100% 线程安全的。一个线程可能会创建一个实例,将引用传递给另一个线程,而第二个线程在调用该getElement方法时会看到实例列表状态的不一致版本。这是因为在构造函数完成和后续方法调用之间没有发生之前同步顺序关系。请参阅 JLS 17.4.4 和 17.4.5。

这可以通过多种方式“修复”。例如:

  • 声明getElementsynchronized方法,
  • 声明elementsvolatile, 或
  • 声明elementsfinal

由于 final 字段的特殊规则,最后一个有效;见 JLS 17.5。(如果ListContainer意图是不可变的,则声明elementsasfinal是最好的解决方案......)


如何确保ElementClass两个不同的线程不会获取对对象的相同引用并冒着意外行为的风险?

这是可能的,但它需要一个额外的数据结构......跟踪哪些ElementClass实例已被“签出”。而且您会遇到客户端不“返回”实例、实现队列等问题。

这可以通过更改ListContainer类来完成吗...

只是困难重重;看上面。

ElementClass... 还是必须在课堂上采取所有线程安全措施?

这是最简单也是最好的方法。如果您需要操作序列的互斥,请使相关(单个)操作同步,或者设计一个用于锁定的“协议”。(“协议”可能只是使用什么锁的“规则”......)

于 2013-06-15T07:17:37.653 回答
0

课堂上需要采取安全措施ElementClass

即使您阻止多个线程getElement()同时调用,这些线程仍可能保留它们收到的引用并在以后使用它。这会引入线程安全问题。

换句话说,即使他们在不同的时间收到了引用,它仍然是同一个引用,将来可以被多个线程同时使用。

于 2013-06-15T07:56:16.650 回答