Как вы можете гарантировать, что несколько потоков могут безопасно получить доступ к полю класса? - PullRequest
10 голосов
/ 23 сентября 2008

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

Это безопасно:

public class SomeClass {
    private int val;

    public synchronized int getVal() {
        return val;
    }

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

или сеттер вызывает дальнейшие осложнения?

Ответы [ 6 ]

17 голосов
/ 23 сентября 2008

Если и здесь вы используете «синхронизированный» на сеттере, этот код является поточно-ориентированным. Однако это может быть не достаточно зернистым; если у вас есть 20 геттеров и сеттеров, и все они синхронизированы, возможно, вы создаете узкое место синхронизации.

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

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

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

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

3 голосов
/ 23 сентября 2008

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

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

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

2 голосов
/ 23 сентября 2008

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

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

1 голос
/ 07 августа 2009

Если ваш класс содержит только одну переменную, тогда другим способом достижения безопасности потока является использование существующего объекта 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 на практике".

0 голосов
/ 23 сентября 2008

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

Пример:

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 (Addison-Wesley))", возможно, http://java.sun.com/docs/books/tutorial/essential/concurrency/index.html также полезно ...

0 голосов
/ 23 сентября 2008

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...