Если у меня есть несколько изменяемых свойств в объекте, на который будут реагировать несколько потоков, я понимаю, что они должны синхронизироваться.
class Doggie {
private String name;
private int age;
public void setName(String name) { this.name = name; }
public String getName() { return this.name; }
public void setAge(int age) { this.age = age; }
public int getAge() { return this.age; }
}
Вопросы:
return
и присвоение атомарные операции в Java?
- Разве в Java не являются атомарными операциями возврата и присваивания?
Да, они атомарны (по крайней мере, в некоторых случаях), но атомарность - не единственная проблема. Другой важный вопрос заключается в том, будет ли действие записи в атрибут одним потоком гарантированно быть видимым для следующего чтения того же атрибута, сделанного другим потоком.
Когда операции чтения и записи выполняются в одном потоке, при чтении гарантированно обнаруживается более ранняя запись.
Когда операции чтения и записи выполняются в разных потоках, при чтении гарантируется только ранняя запись, если два потока синхронизируются правильно ... или если атрибут объявлен как volatile
.
Обратите внимание, что примитивные блокировки / мьютексы - не единственный способ синхронизации.
- Поскольку свойства не обязательно могут быть взаимосвязаны, не всегда имеет смысл синхронизировать с одной и той же блокировкой. Как организовать структуру блокировок?
Имеет смысл использовать несколько блокировок, если (и только если) вероятен конфликт блокировок. В вашем примере конкуренция за блокировку может стать проблемой только в том случае, если некоторый экземпляр Doggie
получает очень высокую скорость операций получения и / или установки.
- Что лучше: внутренняя блокировка или шаблон блокировки частного объекта?
Это зависит от обстоятельств.Если ваше приложение будет использовать примитивную блокировку объекта Doggie
, то вы можете получить конфликт блокировок или даже непреднамеренную блокировку операций get и set. В этом случае рекомендуется использовать частную блокировку. В противном случае личная блокировка - это ненужные накладные расходы.
В вашем примере требуется неизменяемый объект. http://java.sun.com/docs/books/tutorial/essential/concurrency/imstrat.html
Операции со ссылками являются атомарными, но не изменчивыми - вы всегда будете видеть старое значение или новое значение, но нет гарантии, что вы увидите новое значение без какого-либо барьера памяти. Я не могу вспомнить детали того, какие примитивы гарантированно будут атомарными - возможно, почти все, кроме длинных и двойных.
Лично я бы использовал единственный частный замок, пока не увидел никаких доказательств того, что это было узкое место. Я бы посоветовал не блокировать "this", так как другой код тоже может заблокировать его. Если вы единственный код, который знает о замке, вам будет труднее получить помехи. Сказав это, если вызывающие абоненты хотят атомарно изменить более одного свойства, вы можете выставить блокировку через свойство.
Вам определенно нужен изменяемый тип, безопасный для потоков? Если бы вы могли избежать этого требования, это упростило бы жизнь.
Вы правы, если обратите внимание на то, что у несвязанных свойств могут быть разные блокировки. Учитывая, что блокировка объектов требует тривиальной памяти, лично я бы выбрал блокировку на каждое свойство, а не на весь объект.
Легкий способ сделать это - просто иметь булево значение, которое устанавливается, когда свойство записывается, и снимается в противном случае. Тяжелый способ сделать это, чтобы поддерживать таймауты и т.д., - это мьютекс.