7

В последнее время я читаю несколько книг о параллельности Java. Что касается безопасности потоков, если невозможно сделать класс несовместимым, вы всегда можете обеспечить безопасность потока, синхронизируя его данные.Синхронизированная, летучая и потоковая безопасность

Следующий класс будет явно не поточно

public class NotThreadSafe { 
    private int value; 

    public void setValue(int value) { 
    this.value = value; 
    } 

    public int getValue() { 
    return this.value; 
    } 
} 

Тогда я могу синхронизировать записи, но он будет оставаться не Потокобезопасная

public class StillNotThreadSafe { 
    private int value; 

    public synchronized void setValue(int value) { 
    this.value = value; 
    } 

    public int getValue() { 
    return this.value; 
    } 
} 

Как я должен был бы синхронизировать не только пишет также, а также чтения

public class ThreadSafe { 
    private int value; 

    public synchronized void setValue(int value) { 
    this.value = value; 
    } 

    public synchronized int getValue() { 
    return this.value; 
    } 
} 

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

public class NotSure { 
    private volatile int value; 

    public synchronized void setValue(int value) { 
    this.value = value; 
    } 

    public int getValue() { 
    return this.value; 
    } 
} 

Является ли последний класс поточно-??

ответ

6

Короткий ответ:

Да, но вы даже не нужно synchronized в этом последнем случае. Единственное, что делает setValue, - это одиночная операция, запись - и летучие атомы в каждой операции. То есть каждая запись является атомарной, и каждое чтение является атомарным.

Более длинный ответ:

Конечно, если вы должны были попытаться увеличить значение, используя шаблон, как:

NotSure ns = new NotSure(); 
int v = ns.getValue(); 
ns.setValue(v + 1); 

... то это не потокобезопасной, поскольку он включает в себя две операции по ns.value (чтение и запись), тогда как volatile дает вам атомарность для одной операции. В этом случае даже добавление в как у, так и для гейтера и сеттера было бы недостаточно, так как операции могли быть введены между вызовами обоих методов.

Эта последняя точка на самом деле является немного противоположным аргументом в пользу утверждения, что «вы всегда можете обеспечить безопасность потоков, синхронизируя данные [объекта]». Если вы хотите потокобезопасный способ увеличения NotSure.value, вам нужно будет синхронизировать всю операцию инкремента, а не только доступ к данным объекта. И в этом случае вам необходимо будет синхронизировать сеттер, так как он мог бы в тоже время вставлять себя между операциями метода инкремента. Геттер по-прежнему не нужно синхронизировать, так как ключевое слово volatile гарантирует, что получатель получает либо пре-инкрементированное, либо пост-инкрементированное значение.

+0

Предположим, что setValue() делает немного больше, чем это (скажем, 100 значений), и он не завершен в тайм-листе первого потока. Теперь, если второй поток начинает читать, как объявить все переменные как volatile help ?. Как вы сказали, Летучие атомы. Итак, теперь второй поток будет вынужден игнорировать его локальный кеш. Он вынужден получить последнюю ценность. Если вы не используете синхронизацию в этом случае, половина значений, которые обновляются в первом потоке, читаются правильно ?. И мы этого не хотели бы? Поправьте меня, если я ошибаюсь. – TheLostMind

+1

Правильно, я имел в виду 'setValue', как написано в вопросе. Если этот метод сделал больше - если он установил несколько значений или даже написал значение «значение» несколько раз - тогда без 'synchronized', getter (или другой метод) мог бы пройти и прочитать значения, поскольку они находятся на полпути через' setValue 's выполнение, которое, вероятно, было бы плохо. В этот момент все это зависит от конкретных методов, полей, прецедентов/требований и т. Д. – yshavit

+0

Используйте блокировку или синхронизацию на объекте монитора, если операции сложнее. – Radiodef

0

Истинно, нет необходимости в создании методов getValue или setValue как синхронизированный.

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

звонок с геттером или сеттером может быть выполнен одновременно. В случае вызова из двух отдельных потоков (один из них выполняет вызов getter и другой вызов setter.

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

+2

Невозможно синхронизировать переменную/поле, может быть только метод или блок. – yshavit

+0

тем я имею в виду синхронный (значение) что-то вроде ----------- \t Integer valx = 0; \t \t INT getValx() \t { \t \t синхронизированы (valx) \t \t { \t \t \t возвращение valx; \t \t} \t} \t \t недействительными setValx (Int valX) \t { \t \t синхронизированы (valx) \t \t { \t \t \t valx = valX; \t \t} \t} -------- Кажется, мы не можем синхронизироваться по примитивным типам данных. – Acewin

+0

Это тоже не потокобезопасно. Помните, что для того, чтобы 'synchronized' работал, оба вызова должны быть на одном и том же объекте. Если вы начинаете с 'Integer valx = 0', а затем вызываете' setValx (1) ', то при следующем вызове' setValx' или 'getValx' вы можете ссылаться на объект _different_,' valx = 1' , Таким образом, два вызова не синхронизируются друг с другом - он не является потокобезопасным. – yshavit

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