2014-11-02 2 views
0

Если у меня есть список компонентов в многопотоковой среде, и если я выполняю какую-либо операцию в этом списке, кроме add (я использую в этом случае ключевое слово, синхронизированное в списке) и get (метод, вызываемый компонент является потокобезопасным), является ли это потокобезопасным?Thread-safety simple

public class Test { 

    private final ArrayList<Component> myContainer = new ArrayList<Component>(); 

    public void add(Component){ 
     synchronized(myContainer){ 
      myContainer.add(Component) 
     } 
    } 

    public void useComponents() 
    { 
     for(Component c : myContainer) 
      c.use(); // method thread-safe 
    } 

    // no other operations on myContainer 
} 
+1

Предоставьте необходимый код для повторения вашего сценария. В текущем описании код все равно может быть небезопасным. –

+0

Allawys поток небезопасный? @LuiggiMendoza – kaoziun

+0

'useComponents' является небезопасным. Любой другой поток может добавить новый «Компонент», пока вы пересекаете внутренние элементы 'myContainer'. –

ответ

0

Это выглядит нормально, за исключением того, что я не уверен, что поведение итератора в useComponents(), если вы будете одновременно добавлять элементы в список.

Вместо этого вы использовали вместо этого CopyOnWriteArrayList?

1

В текущей форме он не является потокобезопасным: метод useComponents может выполняться одним потоком. В то же время другой поток может вызвать add и, таким образом, модифицировать коллекцию во время ее повторения. (Эта модификация может произойти между двумя звонками до c.use(), поэтому тот факт, что метод use() является потокобезопасным, не поможет вам здесь).

Строго говоря, это даже не ограничивается многопоточности: Если c.use() внутренне называется test.add(someOtherComponent) (! Даже если это было сделано в том же потоке) это было бы бросить ConcurrentModificiationException, потому что опять же, коллекция была изменена в то же время перевернулся.

безопасность резьбы (без Agains безопасности одновременных модификаций) может быть достигнута путем простого обертывания итерации в synchronized блока:

public void useComponents() 
{ 
    synchronized (myContainer) 
    { 
     for(Component c : myContainer) 
      c.use(); // method thread-safe 
    } 

}

Тем не менее, это все еще оставить возможность ConcurrentModificationException. Скорее всего, вызов c.use() не будет (и не должен) модифицировать коллекцию, в которой находится компонент (в противном случае можно было бы спросить дизайн в целом).

Если вы хотите, чтобы позволить c.use() вызов изменить коллекцию, вы могли бы заменить коллекцию с CopyOnWriteArrayList:

private final List<Component> myContainer = 
    new CopyOnWriteArrayList<Component>(); 

, а затем вы можете даже удалить synchroniziation полностью. Но вы должны знать о последствиях: содержимое списка будет скопировано во время каждой модификации (отсюда и название ...). Обычно это используется в случаях, когда у вас есть небольшая коллекция, которая часто повторяется, но которая редко изменяется. (Слушатели во всех формах являются классическим примером здесь).

+0

Я бы порекомендовал использовать другой подход с другой параллельной структурой, такой как 'BlockingQueue', подкрепленной' LinkedBlockingQueue'. –

+0

@LuiggiMendoza Да, пакет http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html содержит некоторые коллекции, которые могли бы * вероятно * использоваться вместо этого.Основываясь на названии вопроса, я хотел указать на основную проблему и показать простейшие альтернативы (хотя они могут и не быть «лучшими» для каждого случая приложения) – Marco13

+0

Я знаю, и мой комментарий должен был просто сказать вам, что вы может добавить другое решение для вашего текущего сообщения. –

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