2014-08-29 5 views
3

Представьте, что мы имеемИспользование летучих коллекций и массивов в Java

volatile int publisher = 0; 
volatile List<String> list = Arrays.asList("Buenos Aires", "Córdoba", "La Plata"); 
volatile String[] array = {"Buenos Aires", "Córdoba", "La Plata"}; 

Насколько я понимаю.

Исходные значения в списке и массиве опубликованы правильно и видны для всех потоков чтения.

Все значения, добавленные после инициализации, не публикуются безопасно.

Тем не менее, мы можем читать и публиковать их безопасного использования

//in Thread 1 
list.add("Safe City"); 
array[2] = "Safe city"; 
publisher = 1; 

//in Thread2 

if(publisher == 1) { 
String city = list.get(3); 
city = array[2]; 
} 

Я прав?

+0

Обратите внимание, что 'volatile' ключевое слово применяется к списку или массива переменной себя, не содержимое списка или массива. –

+0

Непонятно, что вы здесь просите. Для примитивов «volatile» относится к самому первобытному значению. Для объектов ключевое слово 'volatile 'относится только к ссылке, а не к объекту. Поэтому для 'list' и' array' '' volatile' не влияет на содержимое списка или массива. 'volatile' не делает то, что вы, кажется, думаете. –

+1

@JimGarrison в Java 1.5 и новее, volatile гарантирует видимость всех изменений, внесенных до назначения, волатильные, а не только изменения самого взрывателя. Так что это может быть актуально. – Arkadiy

ответ

4

Это правильно, но ...

volatile ключевое слово в списке и массив не имеют никакого отношения здесь - тот факт, что вы пишете значение в volatilepublisherпосле писать другие значения, и читать верните это значение в состояние ìfдо, считывая другие значения во втором потоке, гарантирует согласованность между этими потоками.

Если вы удалите ключевое слово volatile из списка и массива, ваш код по-прежнему будет в безопасности.

Если вы удалите переменную write/read publisher, операция add * и назначение массива более небезопасны.

И да, начальное назначение переменных также безопасно.

* , который в любом случае фактически недействителен в этом конкретном списке, как указано Stuart Marks, но давайте предположим, что это, например, a ArrayList

+0

Спасибо CupawnTae, но если у меня вообще нет «издателя», я исправлю в этом: «Начальные значения в списке и массив публикуются правильно и видны для всех потоков чтения. Все значения, добавленные после инициализации, не публикуются безопасно »? – MiamiBeach

+0

@Dymytry см. Обновленный ответ. Я подробно рассмотрел и должен уточнить – CupawnTae

+1

«если вы добавите запись в изменчивые переменные, даже если значение не изменится, оно снова станет безопасным» - это * неправильно*. Если поток чтения не способен определить, произошла ли запись (что невозможно, если значение одинаков), то нет отношения * произойдет-до *. Запись в переменную 'volatile' делает * not * заставляет другие потоки читать эту переменную, чтобы дождаться записи. Код в вопросе делает это правильно, поскольку переход «издателя» от «0» до «1» обнаруживается * и * проверяется нитью чтения. Без этого он будет сломан. – Holger

0

«Публикация» происходит между потоком, который устанавливает изменчивое значение и поток, который его получает.

Вы должны как

publisher = 1; 

в одном потоке и

int local = publisher; 

в другой.

+0

'if (publisher == 1)', который уже находится в вопросе, достаточно для удовлетворения второго ограничения – CupawnTae

6

Глядя строго на то, что делает код, и не более того, и оценивая его только с точки зрения модели памяти, вы правы. Запись в энергонезависимую переменную publisher в потоке 1 и чтение из энергонезависимых переменных в потоке-учредить происходит, прежде, чем отношения, так что все предыдущие записи из потока 1 будут видны последующее чтением из нити 2.

Как отметил CupawnTae, нет необходимости, чтобы список и массив были неустойчивыми, чтобы это могло удерживаться. Только publisher должен быть неустойчивым.

Рассматривая это с более широкой точки зрения, очень сложно расширить этот код, чтобы сделать что-либо еще.(Отмените тот факт, что List, возвращенный Arrays.asList, не может иметь элементов, добавленных к нему, предположим, что это вместо ArrayList.) Предположительно, поток 1 или какой-либо другой поток захочет продолжить добавлять элементы в список. Если это произойдет, чтобы привести ArrayList к перераспределению его базового массива, это может произойти, когда поток 2 все еще читает результаты предыдущего добавления. Таким образом, противоречивое состояние может быть видимым для потока 2.

Предположим, что поток 1 хочет сделать последующие обновления. Он должен будет установить publisher на какое-то другое значение, скажем 2. Теперь, как читать потоки знают, для чего нужно проверить правильное значение? Ну, они могут читать ожидаемое значение от некоторой другой изменчивой переменной ....

Несомненно, можно построить схему, в которой поток 1 может писать в список (или массив) по желанию, а поток 2 никогда ничего не увидит но непротиворечивые снимки, но вы должны быть исключительно осторожны в отношении видимости памяти на каждом шагу. В определенный момент проще использовать блокировки.

0

Вы рассмотрели использование блоков synchronized для обеспечения блокировки структур данных, которые вы пытаетесь прочитать/записать в/из?

//in Thread 1 
synchronized(someLockingMonitor) { 
    list.add("Safe City"); 
    array[2] = "Safe city"; 
} 

//in Thread2 
synchronized(someLockingMonitor) { 
    String city = list.get(3); 
    city = array[2]; 
} 

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

Если параллелизм важен для вас, то есть вы действительно хотите читать и писать разные темы одновременно, посмотрите на параллельные коллекции в java.util.concurrent.

0

Я знаю, что volatile работает только со ссылкой на объект, но не с изменением содержимого изменяемого объекта.

Но может ли кто-нибудь показать нам пример, чтобы продемонстрировать проблему энергозависимого массива.

Я последовал примеру на https://javax0.wordpress.com/2016/09/21/final-volatile/

public class VolatileDemonstration implements Runnable { 
@Override 
public void run() { 
    System.out.println(getFirst()); 
    while (getFirst() == 0); 
    System.out.println(getFirst()); 
} 

public VolatileDemonstration() throws InterruptedException { 
    new Thread(this).start(); 
    Thread.sleep(1000); 
    setFirst(1); 
} 

public static void main(String[] args) throws InterruptedException { 
    new VolatileDemonstration(); 
} 

private volatile int[] arr = new int[20]; 

public int getFirst() { 
    return arr[0]; 
} 

public void setFirst(int n) { 
    arr[0] = n; 
} 
} 

Но проблема не виснет ....

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