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

StackOverflow https://stackoverflow.com/questions/118371

Вопрос

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

Безопасно ли это:

public class SomeClass {
    private int val;

    public synchronized int getVal() {
        return val;
    }

    private void setVal(int val) {
        this.val = val;
    }
}

или сеттер вносит дополнительные осложнения?

Это было полезно?

Решение

Если вы и здесь используете 'synchronized' в установщике, этот код является потокобезопасным.Однако он может быть недостаточно детализированным;если у вас есть 20 приемников и установщиков, и все они синхронизированы, возможно, вы создаете узкое место синхронизации.

В этом конкретном случае, с одной переменной int, удаление 'synchronized' и пометка поля int 'volatile' также обеспечат видимость (каждый поток будет видеть последнее значение 'val' при вызове средства получения), но оно может быть недостаточно синхронизировано для ваших нужд.Например, ожидая

 int old = someThing.getVal();
 if (old == 1) {
    someThing.setVal(2);
 }

устанавливать значение val равным 2 тогда и только тогда, когда оно уже равно 1, неверно.Для этого вам нужна внешняя блокировка или какой-нибудь атомарный метод сравнения и установки.

Я настоятельно рекомендую вам прочитать Параллелизм Java на практике автор : Брайан Гетц et al, он имеет наилучший охват конструкций параллелизма Java.

Другие советы

В дополнение к Комментарий Коуэна, вы могли бы сделать следующее для сравнения и сохранения:

synchronized(someThing) {
    int old = someThing.getVal();
    if (old == 1) {
        someThing.setVal(2);
    }
}

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

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

Редактировать:Вот такой Ссылка к некоторой дополнительной информации о синхронизации, а что нет.

Если ваш класс содержит только одну переменную, то другим способом достижения потокобезопасности является использование существующего объекта AtomicInteger.

public class ThreadSafeSomeClass {

    private final AtomicInteger value = new AtomicInteger(0);

    public void setValue(int x){
         value.set(x);
    }

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

}

Однако, если вы добавите дополнительные переменные таким образом, чтобы они были зависимыми (состояние одной переменной зависит от состояния другой), то AtomicInteger работать не будет.

Повторяя предложение прочитать "Параллелизм Java на практике".

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

Пример:

public class SomeClass {

  private Object mutex = new Object();
  private int    val   = -1; // TODO: Adjust initialization to a reasonable start
                             //       value
  public int getVal() {
    synchronized ( mutex ) {
      return val;
    }
  }

  private void setVal( int val ) {
    synchronized ( mutex ) {
      this.val = val;
    }
  }
}

Гарантирует, что только один поток считывает или записывает данные в локальный элемент экземпляра.

Прочитайте книгу "Параллельное программирование на Java (tm):Принципы проектирования и шаблоны (Java (Аддисон-Уэсли))", возможно http://java.sun.com/docs/books/tutorial/essential/concurrency/index.html это тоже полезно...

Синхронизация существует для защиты от помех потокам и ошибок согласованности памяти.Синхронизируя getVal(), код гарантирует, что другие синхронизированные методы в SomeClass также не будут выполняться одновременно.Поскольку других синхронизированных методов не существует, это не приносит большой пользы.Также обратите внимание, что операции чтения и записи в примитивах имеют атомарный доступ.Это означает, что при тщательном программировании нет необходимости синхронизировать доступ к полю.

Читать Синхронизация.

Не совсем уверен, почему это значение было снижено до -3.Я просто обобщаю то, что говорится в руководстве по синхронизации от Sun (а также в моем собственном опыте).

Использование простого доступа к атомарным переменным более эффективно, чем доступ к этим переменным через синхронизированный код, но требует большей осторожности со стороны программиста, чтобы избежать согласованности памяти ошибки.Стоит ли прилагать дополнительные усилия , зависит от размера и сложности приложения.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top