используя синхронизированный в потоках - PullRequest
1 голос
/ 30 августа 2009

Какое может быть понимание следующего?
Я прошел через этот пост в SO, но все еще не могу его собрать.

code1:

synchronized(this){ 
// some code
}

code2:

Object lock = new Object();

synchronized(lock){
// some code
}

Какой-нибудь учебник или ссылки для объяснения синхронизированы, как они объясняют это ребенку?

Ответы [ 4 ]

6 голосов
/ 30 августа 2009

По сути, с каждым объектом в Java связана «блокировка».

Когда поток достигает вызова к synchronized (что-то), он должен получить блокировку чего-либо, прежде чем продолжить. Если вы хотите, чтобы только один поток одновременно мог изменять состояние объекта, наиболее очевидная вещь - это синхронизация при блокировке этого объекта. Если разные методы могут вызываться параллельно, для этого нужны разные блокировки.

Если вы пишете синхронизированный (это) или просто синхронизированный, поток должен получить блокировку, связанную с текущим объектом (из которого вызывается метод).

Обратите внимание, что, начиная с Java 5.0, параллельный пакет обеспечивает надлежащие блокировки , которые можно использовать вместо синхронизации.

3 голосов
/ 30 августа 2009

Одна вещь, не упомянутая в других отличных ответах, которые уже даны, это разница между code1 и code2. В code1 синхронизация выполняется для экземпляра объекта, в котором обнаружен код, а в code2 - для конкретного объекта блокировки внутри объекта.

Если в охватывающем классе есть только два синхронизированных блока, между ними нет функциональной функциональной разницы, но учтите это:

class CodeOneClass {
  ...
  synchronized(this) {   // or merely "synchronized" - it defaults to this
      first protected code block
  }
  ...
  synchronized(this) {   
      second protected code block
  }
...
}

class CodeTwoClass {
  ...
  Object lock1 = new Object();
  synchronized(lock1) {   
      first protected code block
  }
  ...
  Object lock2 = new Object();
  synchronized(lock2) {   
      second protected code block
  }
...
}

Если два потока пытаются использовать один и тот же экземпляр CodeOneClass, только один из них может находиться в в одном из двух защищенных блоков кода одновременно.

Но со второй идиомой вы можете сказать, что один поток находится в первом защищенном блоке безопасно, а другой - в другом. Обратите внимание, что если бы блокировки были одинаковыми (обе синхронизировались на одном и том же объекте блокировки), поведение было бы таким же, как и у первого.

Есть и другие различия. Некоторые авторы начинают указывать на проблемы с синхронизацией (this) - я бы указал вам на другой пост здесь на SO: Избежать синхронизации (этого) в Java?

Я настоятельно рекомендую прочитать его и три сообщения, на которые он ссылается.

3 голосов
/ 30 августа 2009

Помещение кода в блок synchronized по существу означает: «Как только этот код начинает выполняться, другой код, который должен использовать этот объект, не может выполняться одновременно».

Итак, если поток № 2 выполняет код в вашем блоке code2, когда дело доходит до кода synchronized(lock), он должен эффективно просматривать все остальные потоки, чтобы убедиться, что никто другой не выполняет «синхронизированный» код. с объектом lock на данный момент. Поток # 1, безусловно, одновременно выполняет некоторый код, но это может быть совершенно не связанный код. Если это так, то поток № 2 может безопасно запустить ваш материал "some code".

Между тем, если поток № 1 попадает в блок synchronized(this), он также должен сделать паузу и посмотреть, используют ли другие потоки this. Если this является тем же объектом, что и lock, у нас есть проблема. Нам сказали, что только один поток может одновременно использовать этот объект (в синхронизированном блоке). Тем не менее, тема №2 уже использует ее. Поток № 1 будет просто ждать ... и ждать ... и ждать ... пока в конце концов поток № 2 не закончится. Тогда мы можем продолжить.

Конечным результатом является то, что одновременно может выполняться только один synchronized блок (с определенным объектом, конечно).

2 голосов
/ 30 августа 2009

Предположим, у вас есть Account объект, у которого есть метод:

void debit(long debitAmount, Account beneficiary) throws InsufficientFundsException
{
   if (accountBalance >= debitAmount) {
      accountBalance -= debitAmount;
      beneficiary.credit(debitAmount);
   }
   else {
      throw new InsufficientFundsException();
   }
}

Теперь предположим, что у вас есть счет с балансом в 100 евро, и вы получаете две попытки дебетовать его на 70 евро. Если два дебета происходят одновременно, вы можете получить условие гонки , например:

  • Первый дебет проверяет остаток на счете: 100> = 70, поэтому успешно
  • Второй дебет проверяет баланс счета: 100> = 70, поэтому успешно
  • Первый дебет выполняется; остаток на счете становится 30
  • Выполняется второй дебет; остаток на счете становится -40. Не допускается

Мы можем предотвратить это ужасное положение, синхронизировав блокировку объекта Account:

void debit(long debitAmount, Account beneficiary) throws InsufficientFundsException
{
   synchronized (this) {
      if (accountBalance >= debitAmount) {
         accountBalance -= debitAmount;
         beneficiary.credit(debitAmount);
      }
      else {
         throw new InsufficientFundsException();
      }
   }
}

Это гарантирует, что проверка баланса счета и дебета не может быть прервана другим тестом баланса счета.

Учебное пособие Sun Java - хорошее место для начала для получения информации о параллелизме и блокировке.

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