2015-09-13 2 views
1

Моей программы выглядит следующим образом:Java Летучая/синхронизация на Список_массивах

Для того, чтобы использовать эту ArrayList в обеих потоках, я знаю два варианта:

  1. Для того, чтобы ArrayList неустойчивым. Однако я прочитал в this article, что изменение переменных волатильно допустимо только в том случае, если оно «Записывает переменную не зависит от ее текущего значения». что я думаю в этом случае (потому что, например, когда вы выполняете операцию добавления в ArrayList, содержимое ArrayList после этой операции зависит от текущего содержимого ArrayList или не так ли?). Также DataUpdater должен время от времени удалять некоторые элементы из списка, и я также читаю, что редактирование изменчивой переменной из разных потоков невозможно.

  2. Чтобы сделать эту ArrayList синхронизированной переменной. Тем не менее, мой DataUpdater будет постоянно обновлять ArrayList, так что не блокирует ли DataListener чтение ArrayList?

Не понял ли я какие-либо понятия здесь или есть ли еще один способ сделать это возможным?

+0

Вместо 'ArrayList' вы можете использовать одну из очередей из пакета' concurrency'. – Titus

+0

Вы не можете сделать ArrayList 'volatile'. Вы не можете сделать объект изменчивым. Единственными вещами на Java, которые могут быть неустойчивыми, являются _fields_. –

ответ

5

Летучие не поможет вам на всех. Значение volatile состоит в том, что изменения, внесенные потоком A в общую переменную, сразу видны в потоке B. Обычно такие изменения могут быть в каком-то кеше, видимом только для потока, который их создал, а volatile просто сообщает JVM не делать кеширования или оптимизации, что приведет к задержке изменения значения.

Так что это не средство синхронизации. Это всего лишь средство обеспечения видимости изменений. Более того, это изменение переменной , а не на объект , на который ссылается эта переменная. То есть, если вы отметите list как volatile, это будет иметь значение только в том случае, если вы назначьте новый список list, а не если вы измените содержание списка!


Ваше другое предложение было сделать ArrayList синхронизированной переменной. Здесь есть заблуждение. Переменные не могут быть синхронизированы. Единственное, что можно синхронизировать, это код - либо целый метод, либо конкретный блок внутри него. Вы используете объект в качестве монитора синхронизации .

Монитор - это сам объект (фактически, это логическая часть объекта, который является монитором), а не переменная. Если вы назначили другой объект той же переменной после синхронизации по старому значению, то у вас не будет доступ к вашему старому монитору.

Но в любом случае это не тот объект, который синхронизирован, это код, который вы решили синхронизировать с использованием этого объекта.

В качестве монитора вы можете использовать list для синхронизации операций с ним. Но вы не можете синхронизировать list.


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

В читальном потоке, вероятно, вы должны сделать что-то вроде:

List<T> listCopy; 

synchronized (list) { 
    listCopy = new ArrayList(list); 
} 

// Use listCopy for displaying the value rather than list 

Это происходит потому, что отображение является потенциально медленно - она ​​может включать I/O, обновление GUI и т.д. Таким образом, чтобы свести к минимуму время блокировки, вы просто скопируйте значения из списка, а затем отпустите монитор, чтобы поток обновления мог выполнять свою работу.


Кроме того, есть много типов объектов в java.util.concurrent упаковке и т.д., которые разработаны, чтобы помочь в подобных ситуациях, когда одна сторона пишет, а другой для чтения. Проверьте документацию - возможно, ConcurrentLinkedDeque будет работать для вас.

+0

Тем не менее, зачем вообще нужна синхронизация? – Xander

+2

@Xander Поскольку операции над списками не являются атомарными. Чтение, выполненное в середине обновления другого потока, может видеть его в несогласованном состоянии. Например, если вы удалите элемент в ArrayList, все остальные элементы будут скопированы на одно место слева. Из-за этого, если ваш читатель читает его в это время, он может пропустить предмет. – RealSkeptic

+0

Делает смысл! Спасибо! – Xander

1

Действительно, ни одно из двух решений не является достаточным. Вы на самом деле нужно синхронизировать полную итерацию на ArrayList, и каждый доступ на запись к ArrayList:

synchronized(list) { 
    for (T t : list) { 
     ... 
    } 
} 

и

synchronized(list) { 
    // read/add/modify the list 
} 
+0

Почему бы не прочитать блокировку записи? – SMA

+0

Это еще одна возможность, но она не сильно изменится, поскольку в ОП есть только один читатель. –

+0

Согласитесь и что, если OP выбирает ConcurrentSkipList? – SMA

0
  1. сделать ArrayList неустойчивым.

Вы не можете сделать ArrayList volatile. Вы не можете сделать объект изменчивым. Единственными вещами на Java, которые могут быть неустойчивыми, являются поля .

В вашем примере, list является неArrayList.

private static ArrayList<T> list; 

list является статического поля из Main класса.

volatile ключевое слово имеет значение только тогда, когда один поток обновления поле, а другой поток затем доступ поля.

Эта строка обновляет списка, но делает не обновления летучего поля:

list.add(e); 

После выполнения этой строки, список изменилось, но поле по-прежнему относится к тому же списку объект.

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