"synchronized (this)" vs. "synchronized ((BaseClass) this)" в Java?
Это преемник моего предыдущего вопроса, Доступна ли эта переменная с помощью синхронизации?
Для следующей программы
Class SubClassB extends SuperClassA {
protected int c;
public void inc() {
synchronized (this) {
c++;
}
}
public void dec() {
synchronized ( (SuperClassA) this) {
c--;
}
}
}
Будет ли доступ к счетчику "c" безопасным потоком? Я не уверен, что в методе "dec()" ли ссылка SuperClassA "this" ссылается на действительный объект для синхронизированного блока? Если да, будут ли два синхронизированных блока блокировать один и тот же объект "this"? (Как мне кажется, "(SuperClassA) это" не равно "this")
Этот странный симулированный код исходит из следующего примера реальной жизни, где SuperClassA - это базовый класс, который не должен быть изменен,
Class SuperClassA {
protected int c;
public void dec() {
synchronized (this) {
c--;
}
}
}
Class SubClassB extends SuperClassA {
public void inc() {
synchronized (this) {
c++;
}
}
public void dec() {
super.dec();
}
}
В этом примере метод "dec()" в SubClassB вызывает свой метод "dec()" суперкласса, который выполняет блокировку объекта "this", который, я полагаю, является "SuperClassA.this". Если заблокированный объект в методе SubClassB "inc()" не совсем то же самое, что и заблокированный объект в методе "dec()" в SubClassB, то мне интересно, унаследованный счетчик "c" в SubClassB НЕ может быть безопасным образом доступ к различным потокам, Я чувствую, что есть некоторые двусмысленности в использовании ссылки "this" в синхронизированных блоках.
В примере реальной жизни, если я хочу, чтобы счетчик "c" SubClassB был потокобезопасным, мне нужно добавить еще один синхронизированный блок в свой метод "dec()" , например,
Class SubClassB extends SuperClassA {
public void inc() {
synchronized (this) {
c++;
}
}
public void dec() {
synchronized (this) {
super.dec();
}
}
}
Но кажется, что такой добавленный блок не изящный и может быть лишним!
Есть ли у кого-нибудь идеи по этим вопросам. Спасибо заранее.
Лоуренс
Ответы
Ответ 1
Будет ли доступ к счетчику "c" безопасным потоком?
Да, он использует тот же объект блокировки.
Я не уверен, что в методе "dec()" ссылка SuperClassA "this" ссылается на действительный объект для синхронизированного блока?
Да.
Если да, будут ли два синхронизированных блока блокировать один и тот же объект "this"? (Как мне кажется, "(SuperClassA) это" не равно "this")
Да. Даже если вы приложите экземпляр к чему-то, к которому он может быть добавлен (даже Object), он все равно будет ссылаться на тот же объект.
[...] Но кажется, что такой добавленный блок не изящный и может быть лишним!
Это избыточно. Дополнительная синхронизация требуется только в том случае, если вы вызываете несколько синхронных методов, а комбинированный эффект должен быть атомарным.
Ответ 2
Код является потокобезопасным, потому что (SomeObject) this
adn this
- это один и тот же объект. Литье не преобразует объект в другой объект.
В коде отсутствует инкапсуляция, поскольку он позволяет любому подклассу получить доступ к защищенному полю c
несинхронизированным способом. Таким образом, любой подкласс может использовать c++
или c--
без какой-либо синхронизации. Поле должно быть закрытым.
Ответ 3
Все три примера правильны в отношении синхронизации.
- Только один монитор связан с любым объектом.
- Литье
this
в базовый класс внутри synchronized
не имеет значения.
- Для одного и того же объекта не имеет значения, вызывается ли
synchronized(this)
в контексте производного класса или базового класса: в обоих случаях используется одна и та же блокировка.
Ответ 4
мне кажется, что "(SuperClassA) это" не равно "this"
Неправильный; синхронизация выполняется на объектах, а кастинг только изменяет тип времени компиляции, не влияет на идентичность объекта.
Таким образом, вам не нужно добавлять дополнительную дополнительную синхронизацию в подкласс.