JavaRush /Курсы /Java Core /Нет независимости нитей! Даешь synchronized!

Нет независимости нитей! Даешь synchronized!

Java Core
7 уровень, 3 лекция
Открыта

— Привет, Амиго! У нас есть панацея – лекарство от всех болезней. Как мы уже успели убедиться – неконтролируемое переключение нитей – это проблема.

— А почему бы нитям самим не решать, когда переключиться на следующую? Сделала все важные дела и «маякует», я – все!

— Разрешать нитям самим управлять своим переключением – еще большая проблема. Вдруг какой-то код не очень красиво написан, и нить никогда сама не отдаст свое «процессорное время». Давным-давно так и было – и это был тихий ужас.

— Ладно. И какое же решение есть?

— Блокировка нитей. И вот как это работает.

Было выяснено, что нити мешают друг другу, когда пытаются сообща работать с общими объектами и/или ресурсами. Как в примере с выводом на консоль: консоль одна, а выводят на нее все нити. Непорядок.

Поэтому был придуман специальный объект – мютекс. Это как табличка на двери туалета «свободно» «занято». Он имеет два состояния – объект свободен и объект занят, или их еще называют заблокирован и разблокирован.

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

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

— А как работать с этим мютексом. Надо создавать специальные объекты?

— Все намного проще. Разработчики Java встроили этот мютекс в класс Object. Тебе даже создавать его не придется. Он есть у каждого объекта. Вот как это все работает:

Код Описание
class MyClass
{
private String name1 = "Оля";
private String name2 = "Лена";

public void swap()
{
synchronized (this)
{
String s = name1;
name1 = name2;
name2 = s;
}
}
}
Метод swap меняет местами значения переменных name1 & name2.

Что же будет если его вызывать из двух нитей одновременно?

Итоговый порядок Код первой нити Код второй нити
String s1 = name1; //Оля
name1 = name2; //Лена
name2 = s1; //Оля

String s2 = name1; //Лена
name1 = name2; //Оля
name2 = s2; //Лена
String s1 = name1; name1 = name2;
name2 = s1;
//исполняется другая нить
//исполняется другая нить
//исполняется другая нить
//нить ждет, пока освободится мютекс
//нить ждет, пока освободится мютекс
//нить ждет, пока освободится мютекс
String s2 = name1; name1 = name2;
name2 = s2;
Итог
Значения переменных были дважды обменяны местами и вернулись на первоначальное место.

Обрати внимание на ключевое слово synchronized.

— Да, а что оно значит?

— Когда одна нить заходит внутрь блока кода, помеченного словом synchronized, то Java-машина тут же блокирует мютекс у объекта, который указан в круглых скобках после слова synchronized. Больше ни одна нить не сможет зайти в этот блок, пока наша нить его не покинет. Как только наша нить выйдет из блока, помеченного synchronized, то мютекс тут же автоматически разблокируется и будет свободен для захвата другой нитью.

Если же мютекс был занят, то наша нить будет стоять на месте и ждать когда он освободится.

— Так просто и так элегантно. Красивое решение.

— Ага. А как ты думаешь, что будет в этом случае?

Код Описание
class MyClass
{
private String name1 = "Оля";
private String name2 = "Лена";

public void swap()
{
synchronized (this)
{
String s = name1;
name1 = name2;
name2 = s;
}
}

public void swap2()
{
synchronized (this)
{
String s = name1;
name1 = name2;
name2 = s;
}
}
}
Методы swap и swap2 имеют один и тот же мютекс – объект this.

Что будет, если одна нить вызовет метод swap, а другая – метод swap2?

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

— Молодец, Амиго! Верное решение!

Хотелось бы обратить твое внимание на то, что словом synchronized может быть помечен как кусок кода, так и метод. Вот что это значит:

Код Что происходит на самом деле
class MyClass
{
private static String name1 = "Оля";
private static String name2 = "Лена";

public synchronized void swap()
{
String s = name1;
name1 = name2;
name2 = s;
}

public static synchronized void swap2()
{
String s = name1;
name1 = name2;
name2 = s;
}
}
class MyClass
{
private static String name1 = "Оля";
private static String name2 = "Лена";

public void swap()
{
synchronized (this)
{
String s = name1;
name1 = name2;
name2 = s;
}
}

public static void swap2()
{
synchronized (MyClass.class)
{
String s = name1;
name1 = name2;
name2 = s;
}
}
}
Комментарии (319)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Euraks Уровень 33
21 октября 2026
🦔
TimJohnzz Уровень 23
3 октября 2026
главное, что нужно понять на этом этапе, по моему мнению, что syncronized блокирует не код в фигурных скобках для других потоков, а именно обьект в круглых скобках и взаимодействие с ним в фигурных synchronized(обьект <- он блокируется для других потоков) {//а это границы в которых замок действует}
Grigoryvvv Уровень 28 Expert
22 сентября 2026
23.09.2026 / 27 уровень
Anton Уровень 35
31 октября 2025
А длч чего тут MyClass.class ?
Bohdan Уровень 13
3 февраля 2026
Это значит что весь класс не будет доступен, пока не освободится нитью, а в случае с this, это распространяется только на конкретный объект
@itArarat Уровень 32
11 февраля 2026
Для static данных — блокировка через class.
Anonymous #3585174 Уровень 33
27 августа 2025
Like
30 мая 2025
Synchronized — ключевое слово в Java, которое используется для управления доступом к общим ресурсам в многопоточных приложениях. Его основная цель — обеспечить, чтобы только один поток одновременно мог выполнять определённый блок кода или метод. Применение synchronized Ключевое слово synchronized помогает предотвратить состояния гонки (race conditions), когда несколько потоков пытаются одновременно изменить один и тот же ресурс, что может привести к некорректным результатам. Synchronized можно применять: - Для методов. Если метод помечен как synchronized, то только один поток может выполнять его в одно и то же время на одном и том же объекте. - Для блоков кода. Можно синхронизировать не весь метод, а только определённый блок внутри него. Преимущества и недостатки Преимущества: - Обеспечивает атомарность операций, что важно для сохранения целостности данных. - Предотвращает переупорядочение операторов кода, что может вызвать проблемы, если не использовать синхронизацию. Недостатки: - Другие потоки вынуждены ждать, пока нужный объект или метод освободится. Это может создавать «узкое место» в программе и снижать скорость работы. - Ключевое слово synchronized не работает с классами и переменными, только с методами и блоками.
Vyacheslav Уровень 30 Expert
13 мая 2025
У объектов есть "монитор" - это как флаг, свободен он или нет. Когда поток входит в блок synchonized(this), то он захватывает этот монитор объекта и другие потоки (с этим же объектом) не смогут работать в этой части кода Ну а если вместо (this) указан (MyClass.class) это означает что захватывается монитор объекта класса
Victor Уровень 50
23 апреля 2025
читаю - Сделала все важные дела и «мяукает»
ВК Уровень 38
10 декабря 2024
Внизу ругают, а мне, внезапно, понравилось. Мьютекс это как табличка на двери туалета - лучшее объяснение мьютекса, которое я когда-либо слышал.
{Java_Shark} Уровень 36
29 октября 2024
++