"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"

Неправильный; синхронизация выполняется на объектах, а кастинг только изменяет тип времени компиляции, не влияет на идентичность объекта.

Таким образом, вам не нужно добавлять дополнительную дополнительную синхронизацию в подкласс.