2013-06-15 2 views
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?

+0

Если вы мутируете объект 'ElementClass' с помощью некоторого метода' ElementClass', вы должны «синхронизировать» этот метод мутации. Это гарантирует, что в то время только один поток сможет мутировать этот объект с помощью этого метода. –

ответ

1

Во-первых, ваш класс (как написано) является фактически неизменным (с точки зрения API), потому что нет операции, которая позволяет что-то изменять список после его создания.

(Вы потенциально могут выполнять мутирует операции на ElementClass экземпляров в списке. Но это не мутирует сам список.)

Если вы сделали добавить операцию мутирует к ListContainer, вам нужно будет сделать что-то, чтобы сделать их потокобезопасными. Но если предположить, что внутренний объект List не «течет», было бы достаточно сделать соответствующие операции ListContainer синхронизированными.


На самом деле класс, написанный не на 100% поточно-безопасный. Один поток может создать экземпляр, передать ссылку на другой поток, а для второго потока - просмотреть несогласованную версию состояния списка экземпляра, когда он вызывает метод getElement. Это происходит потому, что нет происходит до или порядок синхронизации связь между завершением конструктора и последующими вызовами метода. См. JLS 17.4.4 и 17.4.5.

Это может быть «исправлено» несколькими способами. Например:

  • объявить getElement как метод synchronized,
  • объявить elements, как volatile или
  • объявить elements, как final.

Последний работает из-за специальных правил для окончательных полей; см. JLS 17.5. (Если ListContainer предназначено быть неизменным, объявляя elements, как final является лучшим решением ...)


Как я могу убедиться, что та же ссылка на ElementClass объект приобретается не на два разные потоки и рискнуть неожиданным поведением?

Это было бы возможно, но для этого потребовалась бы дополнительная структура данных ... что треки, которые ElementClass экземпляров были «выписаны». И у вас возникнет проблема, когда клиенты не «возвращают» экземпляры, реализуют очереди и т. Д.

Это можно сделать, изменив класс ListContainer ...

Только с большим трудом; см. выше.

... или все меры безопасности нити должны быть приняты в классе ElementClass?

Это самый простой и лучший подход. Либо сделайте соответствующие (индивидуальные) операции синхронизированными, либо разработайте «протокол» для блокировки, если вам требуется взаимное исключение для последовательностей операций. («Протокол», вероятно, всего лишь «правило» для того, какой замок использовать ...)

0

Меры безопасности должны быть приняты в классе ElementClass.

Даже если вы предотвращаете одновременный вызов нескольких потоков getElement(), эти потоки могут по-прежнему сохранять ссылку, которую они получают обратно, и использовать ее позднее. Это привело бы к проблемам безопасности потоков.

Другими словами, хотя они получили ссылку в разное время, она по-прежнему является той же ссылкой, которая может использоваться одновременно несколькими потоками в будущем.

Смежные вопросы