Ответ 1
Прежде всего, забудьте о синхронизированных методах. Так называемый синхронизированный метод...
synchronized AnyType foobar(...) {
doSomething();
}
Это не что иное, как быстрый способ записи:
AnyType foobar(...) {
synchronized(this) {
doSomething();
}
}
В этом случае нет ничего особенного в этом методе. Особенностью является синхронизированный блок, и что делает синхронизированный блок очень простым. Когда JVM выполняет это:
synchronized(foo) {
doSomething();
}
Сначала он вычисляет выражение foo
. Результат должен быть ссылкой на объект. Затем он блокирует объект, выполняет тело блока synchronized
, а затем он разблокирует объект.
Но что заперто? Это может означать меньше, чем вы думаете. Это не мешает другим потокам использовать объект. Это не мешает им получать доступ к полям объекта или, обновляя его поля. Единственное, что предотвращает блокирование объекта, - это предотвращение одновременного блокирования других потоков одним и тем же объектом.
Если поток A пытается ввести synchronized(foo) {...}
, тогда как поток B уже заблокирован foo (либо в том же блоке synchronized
, либо в другом), тогда поток A будет вынужден ждать, пока поток B не освободит блокировку.
Для защиты данных используются блоки synchronized
.
Предположим, что ваша программа имеет некоторую коллекцию объектов, которые могут находиться в разных состояниях. Предположим, что некоторые состояния имеют смысл, но есть и другие состояния, которые не имеют чувственно-недействительных состояний.
Предположим, что поток не может изменить данные из одного действительного состояния в другое действительное состояние без временного создания недопустимого состояния.
Если вы поместите код, который изменяет состояние в блоке synchronized(foo)
, и вы помещаете каждый блок кода, который может видеть состояние в синхронизированный блок, который блокирует один и тот же объект, foo
, тогда вы предотвратите другие потоки от просмотра временного недействительного состояния.