Многопоточность глазами разработчика

Поделиться
HTML-код
  • Опубликовано: 1 июн 2024
  • В этом виде мы изучим теоретические основы и получим практические навыки работы с многопоточностью и параллелизмом на примере языка программирования Java.
    Ссылка на Github репозиторий с примерами кода:
    github.com/proselytear/javaco...
    Дружное сообщество:
    t.me/pse_club
    Материалы для разработчиков:
    proselyte.net/
    Рекомендуемые материалы:
    Java Concurrency In Practice
    www.amazon.com/Java-Concurren...
    JLS - Memory Model
    docs.oracle.com/javase/specs/...
    00:00:00 - Введение
    00:01:20 - Основы многопоточности
    00:09:44 - Самоконтроль (основы многопоточности)
    00:10:06 - Работа с потоками
    00:11:17 - Создание потока с помощью класса Thread
    00:14:06 - Создание потока с помощью интерфейса Runnable
    00:15:27 - Работа с методом Thread.sleep()
    00:16:45 - Работа методом join()
    00:18:54 - Приоритеты потоков
    00:21:17 - daemon потоки
    00:23:27 - Самоконтроль (работа с потоками)
    00:23:50 - Синхронизация данных
    00:28:10 - Мьютексы и ключевое слово synchronized
    00:31:01 - Встроенный монитор (intrisic lock)
    00:32:39 - Использование объекта в качестве монитора
    00:34:19 - Работа с ReentrantLock
    00:41:56 - Работа с Semaphore
    00:45:19 - Работа с Exchanger
    00:47:05 - Deadlock vs Livelock vs Starvation
    00:49:35 - Пример Deadlock
    00:52:38 - Пример Livelock
    00:54:45 - Пример Starvation
    00:56:07 - Механизм CAS
    01:00:42 - Самоконтроль (синхронизация данных)
    01:01:00 - Потокобезопасные коллекции
    01:02:42 - Работа с synchorized ArrayList
    01:02:36 - Работа с BlockingQueue
    01:05:26 - Работа с PriorityQueue
    01:07:06 - Работа с CopyOnWriteArray
    01:10:04 - Работа с ConcurrentHashMap
    01:12:33 - Самоконтроль (потокобезопасные коллекции)
    01:12:48 - Executor Framework
    01:14:47 - Работа с SingleThreadPool
    01:15:54 - Работа с FixedThreadPool
    01:17:35 - Работа со ScheduledThreadPool
    01:19:08 - Работа с СachedThreadPool
    01:20:29 - Концепция Callable и Future
    01:23:31 - ForkJoin Framework
    01:28:25 - Виртуальные потоки
    01:34:39 - Заключение

Комментарии • 147

  • @olzhastokanov
    @olzhastokanov Месяц назад +11

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

  • @dadypavlo
    @dadypavlo 2 месяца назад +15

    Последовательная, структурированная подача.
    На одном дыхании смотрится, спасибо!

  • @54SperMan
    @54SperMan 2 месяца назад +7

    Это лучшее видео про многопоточность!!!!

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад

      Большое спасибо за отзыв!

  • @sergeyplatoshin2412
    @sergeyplatoshin2412 2 месяца назад +10

    Лучшее объяснение что я видел, спасибо!

  • @user-yp3xk2of7e
    @user-yp3xk2of7e 2 месяца назад +4

    Готовлюсь к собеседованиям, как раз изучаю тему многопоточности, и тут такое видео от Евгения.
    Коммент пишу до просмотра, потому что знаю, будет топ!

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад +1

      Спасибо за комментарий и удачи на собеседовании :)

  • @alexandr.goncharov
    @alexandr.goncharov 3 дня назад

    Очень простым язым про сложное. Спасибо!

  • @pheelsav
    @pheelsav 17 дней назад

    Топовый материал + все структурированно!

  • @alevadnaya
    @alevadnaya Месяц назад +1

    Огромное спасибо за ваш труд, Евгений

  • @Devivl
    @Devivl Месяц назад

    Женя, большое спасибо за экспертизу! Очень приятно слушать тебя. Без воды, чётко и с примерами на экране. Такие знания продают, а ты делишься ими безвозмездно. Это просто не передать словами.
    Огромное человеческое спасибо.

    • @EugeneSuleimanov
      @EugeneSuleimanov  Месяц назад +1

      Большое спасибо за поддержку!

  • @markolitovchenko5452
    @markolitovchenko5452 17 дней назад

    Спасибо Евгений 😊

  • @user-fi6nb4vu3o
    @user-fi6nb4vu3o Месяц назад +1

    Супер подача материала, и сам материал - все основы по многопоточности в одном видео 🔥 Спасибо большое!

  • @dmitrysavkin5981
    @dmitrysavkin5981 14 дней назад

    Большое Спасибо. Очень нужная вещь

  • @andarworld8985
    @andarworld8985 2 месяца назад

    Большое спасибо за структурированную, грамотно поданную информацию!

  • @borlok2
    @borlok2 2 месяца назад

    Спасибо, Женя! Огромная работа, ты молодец!)

  • @user-lx8wv4ph6o
    @user-lx8wv4ph6o Месяц назад

    Евгений, спасибо вам за ваш труд! Очень полезное видео👍

  • @olegk6273
    @olegk6273 2 месяца назад

    Евгений, спасибо! Освежил знания. Очень было интересно и не скучно

  • @user-kh2mv4jw8u
    @user-kh2mv4jw8u 2 месяца назад

    Контент как всегда на высоте!
    Спасибо.

  • @edmond-dantes-1796
    @edmond-dantes-1796 2 месяца назад

    Комментарий в поддержку канала. Очень качественная и хорошо структурированная подача. Можно использовать как лекции для новичков)

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад +1

      Большое спасибо за поддержку!

  • @user-ze7my3jm6r
    @user-ze7my3jm6r 2 месяца назад

    Супер! Спасибо, Женя!

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад

      Большое спасибо за отзыв!

  • @elnar_1206
    @elnar_1206 2 месяца назад

    Спасибо за видео про многопоточность! Очень понятно объяснили🔥🔥🔥

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад +1

      Спасибо за отзыв, Эльнар!

  • @user-yn9bj5yd1l
    @user-yn9bj5yd1l 2 месяца назад

    В поддержку канала! Спасибо за видео!

  • @user-ek8mk7gt5c
    @user-ek8mk7gt5c Месяц назад

    Евгений, спасибо за видео и примеры)

  • @qwe3271
    @qwe3271 2 месяца назад

    Спасибо за труды!

  • @davitkostandyan5103
    @davitkostandyan5103 Месяц назад

    Как всегда все максимально понятно, спаси большое за твой труд!

  • @user-zp6lh5jv8e
    @user-zp6lh5jv8e Месяц назад

    спасибо большое! прекрасно поданный материал

    • @EugeneSuleimanov
      @EugeneSuleimanov  Месяц назад +1

      Большое спасибо за отзыв!

  • @Valerikvv80
    @Valerikvv80 2 месяца назад

    Отличное видео. Буду периодически пересматривать

  • @twobeerornottwobeer5973
    @twobeerornottwobeer5973 Месяц назад

    Благодарю за видео, что-то новое подчерпнул для себя. Да и повторенье не помешает.

  • @user-hm1sj2lg3t
    @user-hm1sj2lg3t 2 месяца назад +1

    Имба подъехала)
    Спасибо!

  • @ilyasimonovets4639
    @ilyasimonovets4639 2 месяца назад

    Большое спасибо за видео 🤝

  • @user-ss3yc8cc2y
    @user-ss3yc8cc2y Месяц назад

    Сеппер. Материал прям разжеван. Подача на высоте. Спасибо за труд.

  • @Dominic_Herzog
    @Dominic_Herzog 2 месяца назад

    Спасибо, стало более понятно, особенно, про executors и forkjoin, не разбирался с этим)

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад

      Спасибо за комментарий!
      Рад, что материал оказался полезен.

  • @javaspace9999
    @javaspace9999 2 месяца назад

    Как всегда, лучший 😊

  • @knockjkeee
    @knockjkeee 2 месяца назад

    как всегда -> безупречно!👍

  • @AnatolShalobas
    @AnatolShalobas 26 дней назад +1

    Огромное спасибо за видео.
    Увидел небольшую недоработку хотел бы указать на нее.
    К сожалению на 29:59 некорректный пример для наглядного показа как работает синхронизация для двух разных счетчиков.
    Проблема которую я тут вижу в том, что вы в разных потоках, инкрементируете (пусть и потоконебезопасно) разные счетчики. Т.е. даже теоретически они не смогут повлиять друг на друга, а следовательно будут выводиться всегда верно. Даже если не будут синхронизированы.

    • @EugeneSuleimanov
      @EugeneSuleimanov  26 дней назад +2

      Большое спасибо за отзыв и замечание. Здесь больше упор был на введение в мьютексы, но я должным образом не акцентировал на этом внимание, поэтому этот пример вызывает недоумение.
      Еще раз спасибо!

    • @AnatolShalobas
      @AnatolShalobas 26 дней назад

      @@EugeneSuleimanov вам огромное спасибо за контент!

  • @marksalvatore9257
    @marksalvatore9257 2 месяца назад

    Как раз готовлюсь к собесу. Спасибо большое

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад

      Спасибо за отзыв и удачи на собеседовании.

  • @haykavetisyan5563
    @haykavetisyan5563 2 месяца назад

    Объяснение виртуальных потоков потрясающее! Привет из Армении, дорогой Евгений.

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад

      Большое спасибо за отзыв!

  • @baxiskerimzade2690
    @baxiskerimzade2690 2 месяца назад

    Спасибо большое)

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад +1

      Спасибо за комментарий!

  • @kazbo4431
    @kazbo4431 2 месяца назад

    Это самое годное, что я видел за последнее время (честно)
    Можно предлагать темы для видео ?)

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад

      Спасибо за отзыв!
      Предлагать можно, но уже есть свой план на ближайшие 6 месяцев.

    • @kazbo4431
      @kazbo4431 2 месяца назад

      @@EugeneSuleimanov хотел предложить довольно интересную тему - реализация OTP (one time password / двухфакторка) на спринге. По ней довольно мало материала в интернете, и то, что есть - сделано очень коряво и не секьюрно. Я видел ваши видео, видел вашу архитектуру, и мне кажется, у вас бы получилось реализовать хороший прототип :)

  • @dmitrelkin9256
    @dmitrelkin9256 2 месяца назад

    Шедеврально. Фундаментальная систематизация темы многопоточности. Достойно быть оформлено как раздел учебника. Персональная благодарность за наглядное объяснение использования Conditions у ReentrantLocks.

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад

      Большое спасибо за отзыв!

  • @qaserus1226
    @qaserus1226 2 месяца назад

    спасибо огромное)

  • @caffeinejavacode1475
    @caffeinejavacode1475 5 дней назад

    Полезно!

  • @fara0n333
    @fara0n333 2 месяца назад

    Лайк сразу !)

  • @ktotam8913
    @ktotam8913 2 месяца назад

    Спасибо ❤

  • @ioanntrapeznikov
    @ioanntrapeznikov 2 месяца назад

    Как всегда уровень

  • @user-wc6iz3gj8s
    @user-wc6iz3gj8s 2 дня назад

    Лекция топ

  • @SIGORYS
    @SIGORYS 12 дней назад

    здравствуй, на 34.57 нашел противоречие насчет освобождения мониторов. В Java внутренние мониторы (или мониторы объектов) не могут быть освобождены "любым" потоком. Мониторы объектов обычно захватываются и освобождаются тем же потоком, который вошел в блок synchronized для данного объекта.

  • @chydomonahsilver7855
    @chydomonahsilver7855 2 месяца назад

    Лучший блогер по джава!

  • @nouchance
    @nouchance 2 месяца назад

    Спасибо Евгений! Хочу спросить у вас, точно так как у .NET’e работает потоки как у Java ? Потому что я .NET разработчик.Заранее спасибо!

  • @user-yp3xk2of7e
    @user-yp3xk2of7e 2 месяца назад

    Не могу понять, почему возможна ситуация, когда два раза будет напечатан PING (46:39).
    Если я правильно понял, то поток блокируется до момента, пока не будет выполнен обмен (когда другой поток инициирует обмен).
    Как поток может два раза выполнить: exchange() + print(), если другой поток должен как минимум один раз сделать exchange() + print(), что исключает возможность вывода в консоль дважды например, PING?

  • @alexey9139
    @alexey9139 2 месяца назад +1

    Евгений, спасибо за лекцию - лучшее, что я видел по многопоточке освещающее практически все аспекты.
    Два вопроса:
    1. В ConcurrentHashMap почему блокировка идет не по бакету а по "сегметам из 4х бакетов" - какое логическое объяснение?
    2. В чем смыл SingleThreadPoolExecutor если есть Fixed? доставить из пула "один из одного" не пересоздавая?

    • @themistocies50
      @themistocies50 2 месяца назад

      если бы блокировка была на уровне одного бакета, потребовалось бы больше блокировок и, соответственно, выше были бы накладные расходы на управление ими.
      Так как блокировка происходит на уровне сегментов, несколько потоков могут одновременно обрабатывать различные сегменты карты, что увеличивает производительность многопоточных операций.

    • @alexandr6055
      @alexandr6055 2 месяца назад +1

      Очень хорошие вопросы, надеюсь Евгений ответит

  • @themistocies50
    @themistocies50 2 месяца назад

    34:10 а можно ли назвать эти лок-объекты не мониторами, а именно мьютексами? Насколько я понимаю, реализация монитора происходит с помощью блока synchronized, а в параметры этого блока мы передаем мьютекс, т.е. значение (объект/класс), на котором синхронизируемся?

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад

      Спасибо за вопрос. Да, можно назвать и так, но в Java чаще всего используется термин монитор.

  • @maksimus.ssirotkin1124
    @maksimus.ssirotkin1124 2 месяца назад

    Добрый день и еще такой вопрос, виртуальный поток это абстракция над чем? Я так же смотрел материал где говорится, что инструменты типа corutine and gorutine , это frameworks на уровне языка, позволяющий писать асинхронный код как последовательный

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад

      Спасибо за вопрос!
      Это абстракция над обычными потоками операционной системы.
      Относительно coroutine и goroutine - это механизмы, предоставляемые различными языками программирования (например, Kotlin для coroutine, Go для goroutine), которые обеспечивают лёгкие "зелёные" потоки выполнения. Они позволяют писать асинхронный код так, как будто он выполняется последовательно, что облегчает асинхронное программирование и уменьшает накладные расходы на создание потоков.

    • @maksimus.ssirotkin1124
      @maksimus.ssirotkin1124 2 месяца назад

      @@EugeneSuleimanov тогда, по -сути поток Операционной системы, это время Одного из процессоров, которое выделено , на выполнение задачи., в рамках процесса программы. Но будет ли оно выполнено последовательно или попеременно, Или параллельно, т.е другим процессором или ядром, от нас не зависит, управлять этим нельзя, можно лишь направлять. Я правильно понимаю?

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад

      @@maksimus.ssirotkin1124 по определению потока - не совсем согласен. Но по части контроля - верно, в Java мы не можем это контролировать.

  • @qrthack3233
    @qrthack3233 2 месяца назад

    101 мой друг, хочу в backend, выбираю между PHP, Golang, Node.js, Java, что посоветуешь, чтобы легче было устроиться на работу с перспективой? Или с какого языка посоветуешь начать?

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад +3

      Спасибо за комментарий!
      Я предвзят и советовал бы Java :)

  • @konstantenivanov6235
    @konstantenivanov6235 2 месяца назад

    добрый день.
    Не понял на 34.19
    Как может быть что внутренний монитор может быть освобожден любым потоком?
    ведь если Т1 захватит монитор текущего объекта, то Т2 поток будет ждать пока монитор не освободится.

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад +1

      Добрый день, спасибо за вопрос.
      Как пример - метод notifyAll() у класса Object.

  • @ercefwxdx
    @ercefwxdx 2 месяца назад +1

    Дякую! Супер!

  • @maksimus.ssirotkin1124
    @maksimus.ssirotkin1124 2 месяца назад

    Здравствуйте, такой вопрос, за счет чего достигается ускорение,если потоков больше чем ядер, и другой вопрос, можно ли средствами Java, передать выполнение задачи или пула задач, конкретному процессору или процессору, но чтобы он не отвлекался, на выполнение других задач?

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад

      Спасибо за вопросы!
      По первому вопросу:
      В случае многопоточных вычислений, где задачи могут быть разделены на независимые подзадачи, параллельное выполнение на нескольких потоках может значительно ускорить общее время выполнения.
      По второму вопросу:
      В Java нет прямого способа явно указать процессор, на котором должна выполняться задача.
      Надеюсь, я верно понял вопросы и смог дать на них ответ.

  • @themistocies50
    @themistocies50 2 месяца назад

    Привет! На 30:21 ведь нет необходимости использовать synchronized на методах? Т.к. каждый поток работает со своей переменной, аномалий никаких не происходит.

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад

      Спасибо за вопрос! Да, все верно, здесь просто подводка к внутренним монитором.

  • @user-lk8n0fgjk
    @user-lk8n0fgjk 2 месяца назад

    Поправьте, если не прав, но мне кажется что на 12:03 упоминается пример про SimpleCounterDemo, которого нет в данном видео выше

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад

      Да, вы правы, спасиб за уточнение, вероятнее всего выпало при монтаже.
      github.com/proselytear/javaconcurrency/blob/main/src/main/java/net/proselyte/concurrency/threadsintro/SimpleCounterDemo.java
      Здесь ссылка на этот пример.

  • @user-lk8n0fgjk
    @user-lk8n0fgjk 2 месяца назад

    Евгений, огромное спасибо за видео. С помощью Ваших роликов я разобрался во многих аспектах Java.
    Если возможно, подскажите, пожалуйста, зачем в классе IncrementTwoCountersSynchronizedDemo делать synchronized два метода по инкременту счетчиков? Если каждый поток юзает только свой метод и свой счетчик, и у потоков нет общих ресурсов. Я с десяток раз запускал код без синхронайзд и всегда ответ был один и тот же. Спасибо за ответ!

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад

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

    • @user-lk8n0fgjk
      @user-lk8n0fgjk 2 месяца назад

      @@EugeneSuleimanov Понял, спасибо!

    • @SplashDmg2011
      @SplashDmg2011 Месяц назад

      Вот да, хотел написать такой же коммент, но сначала решил поискать среди имеющихся :) согласен, что пример странный, т.к. синхронизация там вообще ничего не дает.

  • @user-hc9fv1ju7v
    @user-hc9fv1ju7v 2 месяца назад

    Евгений, а где ваши выступления на конференциях посмотреть можно?)

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад

      Спасибо за комментарий!
      По видео не уверен, что созвонились, не самые мейнстримные площадки были.

  • @user-oc7py1vy6s
    @user-oc7py1vy6s 2 месяца назад

    Евгений, спасибо за очень крутое видео. Все очень подробно и доступно показано.
    На 35:38 вы упомянули слово "семантика", это очень сложное слово, не понятно что оно означает. Могли бы вы дать объяснение что оно значит в данном контексте?

    • @ratmirv
      @ratmirv Месяц назад

      Я так понял, что возможно под "семантикой" в данном случае имеется в виду функционал или способ работы/применения.

    • @user-oc7py1vy6s
      @user-oc7py1vy6s Месяц назад

      @@ratmirv в данном контексте "семантика" означает набор гарантий, которые будут выполнены в ходе исполнения программы. Я знал ответ на этот вопрос, прежде чем спрашивать. Просто хотел услышать ответ Евгения. Возможно он будет отличаться от моего определения.

  • @friedbearsburninhell2022
    @friedbearsburninhell2022 2 месяца назад

    Контентище!

  • @bodythanks
    @bodythanks 2 месяца назад +3

    А почему не рассказывает про happens-before?

  • @user-wq9gc4rj3w
    @user-wq9gc4rj3w 13 дней назад

    21:10 а зачем в итоге знать приоритеты если они по факту не работают?

  • @twobeerornottwobeer5973
    @twobeerornottwobeer5973 Месяц назад

    Кажется про volatile вопрос есть, а что это нету

  • @kozhuhovIvan
    @kozhuhovIvan 2 месяца назад

    Разве открытые потоки не нужно закрывать?

    • @EugeneSuleimanov
      @EugeneSuleimanov  2 месяца назад

      Спасибо за вопрос. Не могли бы вы немного уточнить вопрос?

    • @kozhuhovIvan
      @kozhuhovIvan 2 месяца назад

      @@EugeneSuleimanov каждый поток надо закрывать, разве нет?

    • @alexandr6055
      @alexandr6055 2 месяца назад +2

      ​@@kozhuhovIvanон же говорил. В 21 джаве Executors стали импл. Closeable. А если ты про обычные потоки, то нахрена их закрывать, и как ты их закроешь. Метод .stop() давно deprecated

    • @SplashDmg2011
      @SplashDmg2011 Месяц назад

      @kozhuhovIvan Вы, наверное, путаете с ресурсами, которые действительно надо закрывать (например, объект, который работает с файловой системой или сетью). Потоки закрывать не нужно, они завершаются сами, когда завершают выполнение своей работы.

  • @user-xg6so1kq3z
    @user-xg6so1kq3z 2 месяца назад

    +

  • @user-yp3xk2of7e
    @user-yp3xk2of7e 2 месяца назад +1

    Если у кого-то зависает постоянно, как у меня, в примере на 38:38. Попробую объяснить (если не прав, поправьте)
    Если первый поток первый захватывает lock и выполняет firstMethodCalled.signal(), пока второй поток ждёт lock, то когда второй поток получит lock и встанет на ожидание firstMethodCalled.await(), то его некому будет разбудить.
    Вариант решения, как я понял, заключается в оборачивании firstMethodCalled.await() в конструкцию while, где условием будет флаг, который меняет первый поток. Это позволяет добиться:
    1. Не вставать в ожидание, если метод first() выполнился первым
    2. Избежать ложных пробуждений, если метод second() выполнится первым, и поток будет ожидать

    • @ratmirv
      @ratmirv Месяц назад

      У меня такая же проблема, спасибо за решение) Не понимаю, как у автора ни разу не возникло такой ситуации.

    • @qwe3271
      @qwe3271 9 дней назад

      Тоже самое, рандомно паркуются либо Т2 и Т3, либо Т3

    • @qwe3271
      @qwe3271 8 дней назад

      public class Counter {
      private final ReentrantLock lock = new ReentrantLock();
      private final Condition firstMethodCalled = lock.newCondition();
      private final Condition secondMethodCalled = lock.newCondition();
      private final AtomicInteger count1 = new AtomicInteger(0);
      private final AtomicInteger count2 = new AtomicInteger(0);
      public void first() {
      lock.lock();
      try {
      System.out.println("first");
      firstMethodCalled.signal();
      count1.incrementAndGet();
      } finally {
      lock.unlock();
      }
      }
      public void second() {
      lock.lock();
      try {
      while (count1.get() == 0) {
      firstMethodCalled.await();
      }
      System.out.println("second");
      secondMethodCalled.signal();
      count2.incrementAndGet();
      } catch (InterruptedException e) {
      e.printStackTrace();
      } finally {
      lock.unlock();
      }
      }
      public void third() {
      lock.lock();
      try {
      while (count2.get() == 0) {
      secondMethodCalled.await();
      }
      System.out.println("third");
      } catch (InterruptedException e) {
      e.printStackTrace();
      } finally {
      lock.unlock();
      }
      }
      }