Скажем, что у меня синглтон:
public class MySinglton {
private static volatile MySinglton s;
private int x;
public static MySinglton getInstance() {
if (s != null) return s;
synchronized (MySinglton.class) {
if (s == null) {
s = new MySinglton();
}
}
return s;
}
public void setX(int x){
this.x = x;
}
}
Хорошо, метод getInstance является потокобезопасным. Мой вопрос. Нужно ли изменять метод setX, или это потокобезопасно, потому что метод getInsatnce является потокобезопасным. Если это не то, что лучше.
synchronized public void setX(int x){
this.x = x;
}
public void setX(int x){
synchronized (this) {
this.x = x;
}
}
или, наконец,
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void setX(int x){
readWriteLock.readLock().lock();
this.x = x;
readWriteLock.readLock().unlock();
}
Просто потому, что getInstance()
является потокобезопасным, это не означает, что любые другие методы класса являются потокобезопасными. Несколько потоков могут выполнять операции с объектом singleton одновременно.
Вы должны синхронизировать закрытый конечный объект-член класса внутри функции setX
. Не синхронизировать this
и не синхронизировать весь метод. Синхронизация this
синхронизирует объект, и если другой код также синхронизировал объект, возвращенный getInstance()
, вы могли бы зайти в тупик. Помещение синхронизированного ключевого слова до методов означает, что только один синхронизированный метод синхронизированных методов в вашем классе может выполняться потоком экземпляра в любой момент времени. Это также может создать впечатление на клиентов/потребителей этого класса, что класс является потокобезопасным, хотя это может и не быть.
Хорошим подходом к написанию синглтона является использование переименования, поскольку это гарантирует, что только один экземпляр этого будет. Тем не менее, функции-члены перечисления все равно должны быть синхронизированы, если вы хотите, чтобы он был потокобезопасным. См. Страницу 311 "Эффективная Java" на примере: http://uet.vnu.edu.vn/~chauttm/e-books/java/Effective.Java.2nd.Edition.May.2008.3000th.Release.pdf
Нет setX не является потокобезопасным, так как может быть несколько потоков, имеющих ссылку на экземпляр singleton, и они могут попытаться выполнить setX
api в том же экземпляре.
Вам нужно будет сделать setX threadsafe. И ваша реализация метода с синхронизированным ключевым словом будет работать.
Я бы не использовал read lock для чтенияWriteLock, поскольку я пишу ему значение. Также, что, если что-то происходит между вами, вы получаете блокировку и разблокировку? Вы когда-нибудь разблокируете и, следовательно, выйдете в тупик. Поэтому всегда используйте блокировку и разблокировку в блоке try/catch/finally, где вы разблокируете, наконец, блок.
Наличие singleton не препятствует одновременному вызову setX(). Поэтому вам определенно нужна синхронизация здесь.
И кажется неудобным забрать блокировку READ перед ПИСЬМОЙ. Точка: читатели (которые вызывают отсутствующий метод "getX()") нуждаются в чтении; при записи вы хотите блокировать WRITE!
Если быть точным: вам нужна блокировка ReadWrite, если обновляемое свойство не является "атомарным", а ваша программа "знает" разные роли "читателя" и "писателя".
Если есть только метод "setX()" (и нет читателей вокруг, тогда "синхронизированный" должен делать, в этом случае вам не нужна блокировка RW).
Все, что вы нам показали, - это одноэлементный объект с методом setX(int x)
. Но метод setX (x) ничего не делает, кроме как установить приватную переменную, которую не видит другой класс.
Вы не показали нам никакого кода, который использует x. Пока вы не покажете нам, как используется x, "потокобезопасность" ничего не значит.
"Безопасность потоков" заключается в том, чтобы убедиться, что ваша программа будет делать то, что вы хотите, независимо от того, как выполняются чередования потоков. Что вы хотите, чтобы ваша программа выполняла?
Иногда мы говорим о "потокобезопасном" классе. Например, люди говорят, что java.util.concurrent.ArrayBlockingQueue
является "потокобезопасным". Но это означает, что потоки в вашей программе не могут помещать ArrayBlockingQueue
в плохое внутреннее состояние. Это не означает, что очередь всегда будет содержать объекты, которые ваша программа хочет, чтобы они содержались, или в том порядке, в котором ваша программа хочет. Построение программы из "потокобезопасных" объектов не обеспечивает безопасность потока программ.
Какие еще методы вы используете в своем классе MySingleton? Как он должен использоваться?
public final class MySinglton
{
private final static MySinglton s;
private volatile int x;
static {
s = new MySinglton();
}
private MySinglton() {}
public static MySinglton getInstance()
{
return s;
}
public void setX(int x)
{
this.x = x;
}
}
Если у вас нет других требований для вас, вы можете пойти с этим. Предоставьте конструктор по умолчанию, чтобы избежать других экземпляров и пометить класс как final, чтобы никто не мог его расширить. Размещение инициализации в статическом блоке будет работать отлично для большинства программ. Если вы столкнулись с проблемами, просто оберните их в статический внутренний класс.
setX вызывается в экземпляре s. Таким образом, в JVM есть только один случай s в любой момент времени (bcos it a singleton). Если два потока simulataeneely называют setX, они могут оба оверить один и тот же x или шаг друг на друга.
setX не является неявным потокобезопасным.
Это полезно иметь
public void setX(int x){
synchronized (this) {
this.x = x;
}
}
Вот аналогичная пост -узкоспециализированная синхронизация Java в методах getter/setter и singleton pattern класса