Professor Hans Noodles
41 уровень

Синхронизация потоков. Оператор synchronized в Java

Статья из группы Java Developer
участников
Привет! Сегодня продолжим рассматривать особенности многопоточного программирования и поговорим о синхронизации потоков.
Синхронизация потоков. Оператор synchronized - 1
Что же такое «синхронизация»? Вне области программирования под этим подразумевается некая настройка, позволяющая двум устройствам или программам работать совместно. Например, смартфон и компьютер можно синхронизировать с Google-аккаунтом, личный кабинет на сайте — с аккаунтами в социальных сетях, чтобы логиниться с их помощью. У синхронизации потоков похожий смысл: это настройка взаимодействия потоков между собой. В предыдущих лекциях наши потоки жили и работали обособленно друг от друга. Один что-то считал, второй спал, третий выводил что-то на консоль, но друг с другом они не взаимодействовали. В реальных программах такие ситуации редки. Несколько потоков могут активно работать, например, с одним и тем же набором данных и что-то в нем менять. Это создает проблемы. Представь, что несколько потоков записывают текст в одно и то же место — например, в текстовый файл или консоль. Этот файл или консоль в данном случае становится общим ресурсом. Потоки не знают о существовании друг друга, поэтому просто записывают все, что успеют за то время, которое планировщик потоков им выделит. В недавней лекции курса у нас был пример, к чему это приведет, давай его вспомним: Синхронизация потоков. Оператор synchronized - 2Причина кроется в том, что потоки работали с общим ресурсом, консолью, не согласовывая действия друг с другом. Если планировщик потоков выделил время Потоку-1, тот моментально пишет все в консоль. Что там уже успели или не успели написать другие потоки — неважно. Результат, как видишь, плачевный. Поэтому в многопоточном программировании ввели специальное понятие мьютекс (от англ. «mutex», «mutual exclusion» — «взаимное исключение»). Задача мьютекса — обеспечить такой механизм, чтобы доступ к объекту в определенное время был только у одного потока. Если Поток-1 захватил мьютекс объекта А, остальные потоки не получат к нему доступ, чтобы что-то в нем менять. До тех пор, пока мьютекс объекта А не освободится, остальные потоки будут вынуждены ждать. Пример из жизни: представь, что ты и еще 10 незнакомых людей участвуете в тренинге. Вам нужно поочередно высказывать идеи и что-то обсуждать. Но, поскольку друг друга вы видите впервые, чтобы постоянно не перебивать друг друга и не скатываться в гвалт, вы используете правило c «говорящим мячиком»: говорить может только один человек — тот, у кого в руках мячик. Так дискуссия получается адекватной и плодотворной. Так вот, мьютекс, по сути, и есть такой мячик. Если мьютекс объекта находится в руках одного потока, другие потоки не смогут получить доступ к работе с этим объектом. Не нужно ничего делать, чтобы создать мьютекс: он уже встроен в класс Object, а значит, есть у каждого объекта в Java.

Как работает оператор synchronized в Java

Давай познакомимся с новым ключевым словом — synchronized. Им помечается определенный кусок нашего кода. Если блок кода помечен ключевым словом synchronized, это значит, что блок может выполняться только одним потоком одновременно. Синхронизацию можно реализовать по-разному. Например, создать целый синхронизированный метод:
public synchronized void doSomething() {

   //...логика метода
}
Или же написать блок кода, где синхронизация осуществляется по какому-то объекту:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //...какая-то логика, доступная для всех потоков

       synchronized (obj) {

           //логика, которая одновременно доступна только для одного потока
       }
   }
}
Смысл прост. Если один поток зашел внутрь блока кода, который помечен словом synchronized, он моментально захватывает мьютекс объекта, и все другие потоки, которые попытаются зайти в этот же блок или метод вынуждены ждать, пока предыдущий поток не завершит свою работу и не освободит монитор. Синхронизация потоков. Оператор synchronized - 3Кстати! В лекциях курса ты уже видел примеры synchronized, но они выглядели иначе:
public void swap()
{
   synchronized (this)
   {
       //...логика метода
   }
}
Тема для тебя новая, и путаница с синтаксисом, само собой, первое время будет. Поэтому запомни сразу, чтобы не путаться потом в способах написания. Эти два способа записи означают одно и то же:
public void swap() {

   synchronized (this)
   {
       //...логика метода
   }
}


public synchronized void swap() {

   }
}
В первом случае создаешь синхронизированный блок кода сразу же при входе в метод. Он синхронизируется по объекту this, то есть по текущему объекту. А во втором примере вешаешь слово synchronized на весь метод. Тут уже нет нужды явно указывать какой-то объект, по которому осуществляется синхронизация. Раз словом помечен целый метод, этот метод автоматически будет синхронизированным для всех объектов класса. Не будем углубляться в рассуждения, какой способ лучше. Пока выбирай то, что больше нравится :) Главное — помни: объявить метод синхронизированным можно только тогда, когда вся логика внутри него выполняется одним потоком одновременно. Например, в этом случае сделать метод doSomething() синхронизированным будет ошибкой:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //...какая-то логика, доступная для всех потоков

       synchronized (obj) {

           //логика, которая одновременно доступна только для одного потока
       }
   }
}
Как видишь, кусочек метода содержит логику, для которой синхронизация не обязательна. Код в нем могут выполнять несколько потоков одновременно, а все критически важные места выделены в отдельный блок synchronized. И еще один момент. Давай рассмотрим «под микроскопом» наш пример из лекции с обменом именами:
public void swap()
{
   synchronized (this)
   {
       //...логика метода
   }
}
Обрати внимание: синхронизация проводится по this. То есть по конкретному объекту MyClass. Представь, что у нас есть 2 потока (Thread-1 и Thread-2) и всего один объект MyClass myClass. В этом случае, если Thread-1 вызовет метод myClass.swap(), мьютекс объекта будет занят, и Thread-2 при попытке вызвать myClass.swap() повиснет в ожидании, когда мьютекс освободится. Если же у нас будет 2 потока и 2 объекта MyClassmyClass1 и myClass2 — на разных объектах наши потоки спокойно смогут одновременно выполнять синхронизированные методы. Первый поток выполняет:
myClass1.swap();
Второй выполняет:
myClass2.swap();
В этом случае ключевое слово synchronized внутри метода swap() не повлияет на работу программы, поскольку синхронизация осуществляется по конкретному объекту. А в последнем случае объектов у нас 2. Поэтому потоки не создают друг другу проблем. Ведь у двух объектов есть 2 разных мьютекса, и их захват не зависит друг от друга.

Особенности синхронизации в статических методах

А что делать, если нужно синхронизировать статический метод?
class MyClass {
   private static String name1 = "Оля";
   private static String name2 = "Лена";

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

}
Непонятно, что будет выполнять роль мьютекса в этом случае. Ведь мы уже определились, что у каждого объекта есть мьютекс. Но проблема в том, что для вызова статического метода MyClass.swap() нам не нужны объекты: метод-то статический! И что дальше? :/ На самом деле, проблемы в этом нет. Создатели Java обо всем позаботились :) Если метод, в котором содержится критически важная «многопоточная» логика, статический, синхронизация будет осуществляться по классу. Для большей ясности, приведенный выше код можно переписать так:
class MyClass {
   private static String name1 = "Оля";
   private static String name2 = "Лена";

   public static void swap() {

       synchronized (MyClass.class) {
           String s = name1;
           name1 = name2;
           name2 = s;
       }
   }

}
В принципе, ты мог до этого додуматься самостоятельно: раз объектов нет, значит механизм синхронизации должен быть как-то «зашит» в сами классы. Так оно и есть: по классам тоже можно синхронизироваться.
Комментарии (107)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Максим
Уровень 20
19 октября, 21:10
"...Раз словом помечен целый метод, этот метод автоматически будет синхронизированным для всех объектов класса..." - мне кажется очень путано написано. Лучше: "Для всех нитей, использующий метод объекта (instance) он будет синхронизирован на этом объекте (instance)". Что это будет для любого instance это ИМХО и так понятно, а что не у всех объектов будет один lock - это может путать.
Vlad
Уровень 21
29 июня, 12:42
задачу конечно решил, но не путем таких умозаключений, скорее интуитивно...видимо еще рано, нужно руки набивать вместе с шишками))))
Rylero
Уровень 33
15 апреля, 16:24
Непонятно, что имеется ввиду под блокировкой участка кода и объекта - блокируется весь объект со всеми полями, или конкретный участок кода? И если у меня два разных метода с директивой synchronize, но работают они с одними и теми же полями объекта, то в таком случае мьютексы помогут если два разных потока вызовут два этих метода?
Shaman_2010
Уровень 25
26 апреля, 08:17
Перед входом в synchronized метод (или synchronized участок кода в методе) производится захват монитора. В случае с нестатическими методами захватывается монитор экземпляра (this). При этом остальные synchronized методы, использующие монитор того же самого объекта (this) так же окажутся заблокированными, пока не будет освобожден монитор. Но никто не мешает для разных методов использовать мониторы разных объектов. Тогда методы будут лочиться по разным мониторам. Например так:
class MyClass {
   private Object mutex1 = new Object();
   private Object mutex2 = new Object();

   public void swap() {
      ...
      synchronized(mutex1) {
         // Код исполняющийся одной нитью с
         // захватом монитора объекта mutex1
      }
      ...
   }

   public void add() {
      ...
      synchronized(mutex2) {
          // При этом данный код останется доступным для других нитей
          // так как монитор объекта mutex2 будет все еще свободен
      }
      ...
   }
}
А в статических методах класса нет ссылки на экземпляр this, поэтому статический метод по умолчанию захватывает монитор объекта класса Class<T>, где T - это наш класс, в котором находится наш статический синхронизированный метод. Этот объект класса Class<T> общий для всех объектов класса T, который можно получить через конструкцию T.class. Но и в случае со статическими методами нам ничто не мешает использовать мониторы разных доступных статических объектов того же класса (или любого другого).
Иван Плотников
Уровень 41
16 июня, 09:46
Спасибо! Очень в задаче помог ваш комментарий!
Shaman_2010
Уровень 25
17 июня, 17:47
Рад, что смог помочь!🤝
Евгений
Уровень 24
24 февраля, 19:25
все равно не понял этот .class надо еще чуть глубже капнуть
Александр
Уровень 32
3 февраля, 20:59
Граждане, говорю вам как уже год отработавший и перешедший в мидлы - хотите понять многопоточку - посмотрите Ваню Головача. Истинно великий человек! Потом раскошельтесь на 1000 деревянных и купите курс Заура Трегулова. JAVA rush - это про задачи. Это не про теорию.
otreyo
Уровень 38
13 марта, 05:58
прямо респект. давно искал подобного человека, который может нормально понятно все объяснять. Головач реально очень хорошо и подробно объясняет. сколько разных видео смотрел раньше, везде что то не то, везде много вопросов остается, много непонятного. а тут прямо - то что нужно. все подробно понятно последовательно разжевано.
Andrey Sanin
Уровень 29
16 марта, 14:27
1000? Там, кажется, другая цена... Подскажи, как называется курс?
Vladimir
Уровень 19
12 октября 2021, 06:20
Можно согласиться с Евгенеем, хотелось бы получить эту инфу перед задачами...так как я не понимал почему synchronized(Class.class)... Хотя если посмотреть на ситуацию по другому легло это сейчас хорошо, т.к. получил востребованные знания, в отличии от того когда тебе дали информацию и ты читая не принимаешь т.к. пока не видишь прикладного назначения
MisterMisix
Уровень 36
3 ноября 2021, 13:10
Но пока этого не сделано, не сложно же забежать к профессору, посмотреть на доп. материалы, если актуально - изучить - вернуться к задачам? Материи то хоть отбавляй...
Руслан
Уровень 30
22 июля 2021, 05:33
Лекция норм. Я сначала читаю, потом решаю)))
Alikhan
Уровень 20
16 сентября 2021, 10:36
Где нашел ее то
Dmitry
Уровень 20
28 февраля 2021, 09:50
По мне-так норм статья.
Евгений
Уровень 22
17 февраля 2021, 05:21
"В принципе, ты мог до этого додуматься самостоятельно..." Да, что вы говорите? Как это я сам не додумался. По данной логике, я мог вообще открыть IDE, чего то начать тыкать и до всей многопоточности додуматься самостоятельно! Бестолковая статья. Дана уже после задач, когда мы реально сами до всего додумываемся, с помощью других ресурсов. Тут идет непрекращающийся уже годами холивор о том, почему теория зачастую дается после практики и о том, что приходится гуглить самим многое. Соответственно есть 2 лагеря учеников: 1. Все норм, так специально задумано, чтобы мы гуглить учились 2. Такой метод не работает. Заплатили деньги, хотим сразу готовый материал. А мне ту видится причина номер три - вот курс так бестолково слепили в начале и просто никто ничего делать не хочет. Все равно же деньги заносят студенты.
Alexander
Уровень 19
18 февраля 2021, 10:39
Додуматься самостоятельно вполне себе можно было Статья была мне весьма полезной Считаю этот подход направлен на закрепление материала
Ivan
Уровень 22
25 февраля 2021, 18:28
Подход "сначала давать задачи, а потом теорию" направлен на закрепление материала? Какого именно материала, если в лекциях его почти нет? Додуматься можно, если уже есть опыт программирования. А если его нет? И почему я должен изобретать велосипед, если заплатил деньги за обучающий курс? Я теперь взял за правило сначала открывать несколько уровней наперед, а потом уже решать задачи. Только сейчас приступаю к задачам 16 и 17 уровней.
Alexander
Уровень 19
26 февраля 2021, 14:14
Ну смотри. Сначала тебя вынуждают искать инфу самому, это может напрягать, но выхода другого нету, тебе придется этим заниматься. И вот ты в поте лица надыбал инфы и с гордостью решил задачу. После таких стараний информация усвоиться лучше, ибо тебе ее не на блюдечке предоставили, а ты приложил к этому усилия. Дальше идет решение других задач и вроде ты уже начинаешь забывать о чем ты там смотрел, НО ТУТ... Тут тебе профессор подкидывает инфы, которую ты уже вроде как знаешь, но 1) быть может не в полной мере знаешь 2) он шлифует твои знания и если ты вдруг чего неправильно понял, на этом этапе такие казусы должны пропадать 3) повторение - мать учения. После того, как ты прочитаешь такую лекцию о казалось бы уже известных тебе моментах, эти самые моменты намного прочнее осядут в твоей памяти. Я лично вижу систему обучения здесь именно такой, похожей на универ. Знаешь, как о нем говорят: университет дает вам 30% знаний, остальное - самообучение (это не дословно, но как то так и говорят :D). Быть может, многие со мной не согласятся, но мне удобно так заниматься. Иногда безумно лень искать всякую дичь по левым сайтам, соглашусь, но таков путь :D И да, у меня опыта программирования нету.
Ivan
Уровень 22
26 февраля 2021, 18:26
"После таких стараний информация усвоиться лучше, ибо тебе ее не на блюдечке предоставили, а ты приложил к этому усилия". Кому как... У меня информация со всяких левых сайтов и форумов не усваивается. Нет систематизации, нет понимания сути. Да и написано это может быть кем угодно - хоть такими же дилетантами. Можно чужих ошибок нахвататься. Я считаю, что статьи надо читать, когда уже есть некоторая база. А сейчас приходится разбираться по учебникам Шилдта и Head First, и это отнимает уйму времени, т.к. там много лишнего. И когда после учебников я читаю статьи JavaRush по тем же темам, то не нахожу в них ничего нового, зато часто возникает ощущение "вот эту статью бы пару лекций назад!". Что касается закрепления материала, то классическая схема "сначала теория - потом практика" справляется с этим ничуть не хуже. Можно давать и задачи "со звездочкой", но они должны быть лишь дополнением.
Roman Grand
Уровень 35
17 марта 2021, 18:39
Я вот вечно удивляюсь таким "лагерям недовольных" в жизни в целом, и на JR в частности))) Вас кто-то заставлял учиться именно здесь? Ну не нравится подача - найдите место где понравится! Нет же, б%^#ь, вы будете ныть, и ныть, и ныть... Это не так, то не так! Есть нытики, а есть те, которые молча пилят свой (новый) проект, который исправляет недостатки имеющихся (по их мнению), и на этой новой реализации зарабатывают себе имя, деньги и другие радости) "У человека можно отнять все, кроме одного: последней свободы человека — выбирать собственное отношение к любым обстоятельствам, выбирать собственный путь." Виктор Франкл
Ivan
Уровень 22
18 марта 2021, 14:19
Я заплатил деньги за этот курс и имею право высказать свое мнение. И это не нытьё, а конструктивная критика, направленное на улучшение качества курса. Если все будут молчать и только позитивно отзываться, то разработчики сочтут, что всё идеально, а это далеко не так. Я вот удивляюсь таким людям, которые вечно всё терпят, даже когда им что-то не нравится, и всегда боятся высказать своё мнение. "Истина, любит критику, от нее она только выигры­вает, ложь боится критики, ибо проигрывает от нее". Дени Дидро
Roman Grand
Уровень 35
18 марта 2021, 20:38
while (нытьё == критика) {
    1. идеала - не существует, это утопия;
    2. кому-то подходит данная система обучения, кому-то - не подходит;
    3. не подходит - не нужно покупать;
    4. купил - не ной;
    5. хочешь повторить то, что не ноешь, а выражаешь конструктивную критику,
        дабы повлиять на разработчиков - возвращайся к пункту номер 1;
}
Ivan
Уровень 22
19 марта 2021, 12:03
Роман, ответ креативный, но не логичный. Нравится тебе молчать в тряпочку, когда тебе подсовывают халтуру - ну, молчи на здоровье, это твое право, но не надо другим рот затыкать. Каждый имеет право высказывать свое мнение, в том числе и негативное. А без критики никакого развития не было бы. PS Холивар не будем разводить. Желаю тебе успехов в твоем проекте!
Roman Grand
Уровень 35
19 марта 2021, 14:45
Забегая вперёд - благодарю за пожелание, и тебе желаю успехов)) Но, не могу тебя не поправить... Во первых. Если мне что-то не нравится - я никогда не молчу в тряпочку (странно вообще, что ты принял меня за молчуна, после того как я отреагировал на "ваше нытьё"). То есть, если мне обещают одно, а по факту предоставляют совсем другое, я никогда себя не сдерживаю ни в словах ни в действиях. Во вторых. Я никого не затыкал, а высказал своё негодование, увидев неадекватную реакцию людей (субъективно моё мнение, на которое я также имею право). Я, в отличие от "лагеря недовольных", ни разу ещё не встретился с несоответствием обещанного и полученного. Может вам что-то другое подсовывают, мне не известно... Данные лекции (которые так необходимы "вам" для понимания непонятого) лежат открыто на том же ресурсе, на котором вы проходите обучение (и найти их не составляет никакого труда). Да, оно (обучение) здесь не совсем линейно, но никто и не обещал "идеального для всех" формата. Сколько людей, столько и мнений. И если разрабы прислушаются к вам, и изменят формат на линейный, который подходит именно вам, то недовольными станут другие - это замкнутый круг. Не нравится эта платформа, ну кто же вам мешает её не использовать?!) Есть куча других, среди которых нет ни одной, которая подходила бы всем и сразу одновременно.
Oleksii
Уровень 36
8 мая 2021, 10:58
Гибкий ЗОЖ 22 год · Психическое здоровье не менее важно, чем физическое, и наша рубрика #гибкаяпсихология. “Нытье делает нас глупее” «Важно понимать, что мозг в чем-то похож на мышцу. Положительная активность тренирует его. Отрицательная — ослабляет» — так Тревор Блэйк подвёл итог своего исследования, показавшего, что нытьё других людей делает нас глупее. Получасовое воздействие нытья, причём не обязательно личного (СМИ тоже сойдут) приводит гиппокамп головного мозга в замешательство: он лишается части нейронов, ответственных за принятие решений, и в голове образуется «каша». Причем под нытьем подразумевается именно нытьё — печальные жалобы на жизнь без малейшей попытки найти решение проблем. Да, нужно сократить к минимуму такую роскошь как поныть...ну или ныть конструктивно.
Anonymous #2489173
Уровень 35
9 июня 2021, 01:27
Я, в отличие от "лагеря недовольных", ни разу ещё не встретился с несоответствием обещанного и полученного
Ну повезло вам.
Не нравится эта платформа, ну кто же вам мешает её не использовать?!)
Оплаченная подписка? Это как говорить "ну нашёл в супе таракана, да и второе протухшее принесли, ну зачем ты жалуешься? Иди в другой ресторан, а деньжата оставь."
Roman Grand
Уровень 35
13 июня 2021, 14:21
несколько лет проработав в сфере услуг, точнее продавая их - убедился, что чем дешевле клиент, тем более он претенциозный - постоянно что-то не нравится, в том числе, собственный волос, упавший со своей головы, но в только что купленный суп за 3 копейки... только не нужно мне ничего объяснять)) не утруждайтесь... когда поймёте, из чего складывается ценность продукта, и как она сравнивается с рыночными предложениями, тогда и поговорим) хотя нет, не поговорим, в споре с некоторыми людьми, порой, рождается истинная глупость) у меня нет столько свободного времени на пустые разговоры с вами... моё время == мои деньги
Anonymous #2489173
Уровень 35
15 июня 2021, 08:27
Ой бля, какая-то обслуга из убогого новосиба решила попонтоваться) Может я и дешёвый, но моя недвижимость весьма дорогая. И мои деньги получают те заведения и поставщики услуг, которые не пытаются меня и мои волосы анализировать, а сосредоточены на сервисе ;) Времени нет, но на этот коммент время ж нашёл. Хорошо что я зарабатываю достаточно, чтобы писать хоть миллион комментариев - не обеднею.
Руслан
Уровень 30
22 июля 2021, 05:32
Я сначала читаю лекции, потом решаю задачи. Да я хитрец)))
Nik безработный
17 ноября 2021, 08:42
Когда видишь непонятное в задаче и гуглишь, материал усваивается намного лучше! Было так задумано или нет, нужно извлекать пользу из всего, из чего можно. Для меня почитать лекцию и посмотреть примеры, как на других сайтах - вообще ничего не даёт, если нет проблемы, которую надо решить!
Java Rush_3
Уровень 32
27 декабря 2021, 07:04
У тебя никогда не будет денег, потому что, ты - лох
Павел Фролов
Уровень 19
24 января 2021, 13:59
Спасибо огромное, все бы так объясняли!!!)))