в каком классе или интерфейсе определены методы wait notify и notifyall
BestProg
Перед изучением данной темы рекомендуется ознакомиться со следующей темой:
Содержание
Поиск на других ресурсах:
Для обеспечения взаимодействия между потоками выполнения в классе Object реализованы 3 метода.
Согласно документации Java общая наиболее распространенная реализация метода следующая:
В языке Java метод wait() имеет другие перегружены реализации, которые позволяют ожидать указанный период времени.
2. Метод notify() — возобновляет выполнение потока, из которого был вызван метод wait() для того же объекта.
Согласно документации Java общая форма метода следующая:
3. Метод notifyAll() — возобновляет выполнение всех потоков, из которых был вызван метод wait() для того же объекта. Управление передается одному из этих потоков.
Общая форма метода следующая:
2. Схема (рисунок) взаимодействия между двумя параллельными потоками
Процесс синхронизированного взаимодействия между потоками может повторяться сколько угодно.
3. Реализация системы опроса между потоками выполнения. Пример обмена значениями между двумя потоками, которые выполняются параллельно
Условие задачи. Реализовать обмен значением между двумя потоками, которые выполняются параллельно. Первый поток формирует некоторое случайное число. Второй поток ожидает, пока число не будет сформировано первым потоком. Затем это число передается во второй поток и там выводится. После этого управление вновь передается первому потоку для формирования следующего случайного числа. Процесс повторяется заданное количество раз.
Решение. Для решения данной задачи нужно сформировать 3 класса:
Результат выполнения программы
4. Пример последовательного синхронизированного использования общего массива чисел между потоками. Реализация системы опроса
Решение данного примера можно использовать в качестве шаблона для реализации взаимодействия между собственно разработанными потоками.
Условие задачи. Реализовать доступ к массиву поочередно из двух потоков. Первый поток формирует массив чисел указанной длины. Второй поток считывает этот массив чисел. Второй поток ожидает, когда произойдет формирование массива чисел первым потоком. Точно также первый поток должен ожидать до тех пор, пока второй поток не завершит чтение массива.
Продемонстрировать взаимодействие потоков выполнения указанное количество раз.
Решение. Для решения задачи нужно реализовать 3 класса:
В классе Array реализованы следующие члены класса:
методы wait и notify() в Java
Узнайте, как использовать wait() и notify() для решения проблем синхронизации в Java.
1. введение
В этой статье мы рассмотрим один из самых фундаментальных механизмов в Java – синхронизацию потоков.
Сначала мы обсудим некоторые основные термины и методологии, связанные с параллелизмом.
Дальнейшее чтение:
Руководство по ключевому слову Synchronized в Java
Параллелизм Java
Как запустить поток в Java
И мы разработаем простое приложение, в котором мы будем решать проблемы параллелизма с целью лучшего понимания wait() и notify().
2. Синхронизация потоков в Java
В многопоточной среде несколько потоков могут попытаться изменить один и тот же ресурс. Если потоки не управляются должным образом, это, конечно, приведет к проблемам с согласованностью.
2.1. Охраняемые блоки в Java
Один из инструментов, который мы можем использовать для координации действий нескольких потоков в Java, – это защищенные блоки. Такие блоки перед возобновлением выполнения проверяют наличие определенного условия.
Имея это в виду, мы воспользуемся:
Это можно лучше понять из следующей диаграммы, на которой показан жизненный цикл Нить :
Обратите внимание, что существует множество способов управления этим жизненным циклом; однако в этой статье мы сосредоточимся только на wait() и notify().
3. Метод wait()
Обратите внимание, что только один активный поток может одновременно владеть монитором объекта.
Этот метод wait() поставляется с тремя перегруженными сигнатурами. Давайте взглянем на это.
3.1. подождите()
3.2. ожидание(длительный тайм-аут)
Используя этот метод, мы можем указать тайм-аут, после которого поток будет автоматически пробужден. Поток можно разбудить до достижения тайм-аута с помощью notify() или notifyAll().
Обратите внимание, что вызов wait(0) совпадает с вызовом wait().
3.3. ожидание(длительный тайм-аут, int nanos)
Это еще одна сигнатура, обеспечивающая ту же функциональность, с той лишь разницей, что мы можем обеспечить более высокую точность.
Общий период ожидания (в наносекундах) рассчитывается как 1_000_000*тайм-аут + наносекунды.
4. notify() и notifyAll()
Метод notify() используется для пробуждения потоков, ожидающих доступа к монитору этого объекта.
Существует два способа уведомления ожидающих потоков.
4.1. уведомить()
Для всех потоков, ожидающих на мониторе этого объекта (используя любой из методов wait () ), метод notify() уведомляет любой из них о произвольном пробуждении. Выбор того, какой именно поток следует разбудить, не является детерминированным и зависит от реализации.
4.2. Уведомить всех()
Этот метод просто пробуждает все потоки, ожидающие на мониторе этого объекта.
Пробужденные потоки завершатся обычным образом – как и любой другой поток.
Но прежде чем мы позволим продолжить их выполнение, всегда определите быструю проверку условия, необходимого для продолжения потока – потому что могут возникнуть некоторые ситуации, когда поток был разбужен без получения уведомления (этот сценарий обсуждается позже в примере).
5. Проблема Синхронизации Отправителя И Получателя
Давайте разберемся, что здесь происходит:
Отправитель использует send() метод для отправки данных Получателю :
Аналогично, Receiver будет использовать метод receive() :
5.1. Зачем заключать wait() в цикл while?
Поскольку notify() и notifyAll() случайным образом пробуждает потоки, ожидающие на мониторе этого объекта, не всегда важно, что условие выполнено. Иногда может случиться так, что поток просыпается, но условие на самом деле еще не выполнено.
Мы также можем определить проверку, чтобы спасти нас от ложных пробуждений – когда поток может проснуться от ожидания, даже не получив уведомления.
5.2. Зачем нам нужно синхронизировать методы send() и receive ()?
Теперь мы создадим Sender и Receiver и реализуем интерфейс Runnable на обоих, чтобы их экземпляры могли выполняться потоком.
Давайте сначала посмотрим, как будет работать Отправитель :
Для этого Отправитель :
Наконец, давайте реализуем наш Приемник :
Здесь мы просто вызываем load.receive() в цикле, пока не получим последний “End” пакет данных.
Давайте теперь посмотрим на это приложение в действии:
Мы получим следующий результат:
И вот мы здесь – мы получили все пакеты данных в правильном, последовательном порядке и успешно установили правильную связь между нашим отправителем и получателем.
6. Заключение
В этой статье мы обсудили некоторые основные концепции синхронизации в Java; более конкретно, мы сосредоточились на том, как мы можем использовать wait() и notify() для решения интересных проблем синхронизации. И, наконец, мы рассмотрели пример кода, в котором мы применили эти концепции на практике.
Как всегда, полные фрагменты кода, используемые в этой статье, доступны на GitHub.
В каком классе или интерфейсе определены методы wait notify и notifyall
Расскажите о модели памяти Java?
Модель памяти Java (Java Memory Model, JMM) описывает поведение потоков в среде исполнения Java. Это часть семантики языка Java, набор правил, описывающий выполнение многопоточных программ и правил, по которым потоки могут взаимодействовать друг с другом посредством основной памяти.
Существует несколько основных правил для отношения happens-before:
Можно выделить несколько основных областей, имеющих отношение к модели памяти:
Видимость (visibility). Один поток может в какой-то момент временно сохранить значение некоторых полей не в основную память, а в регистры или локальный кэш процессора, таким образом второй поток, выполняемый на другом процессоре, читая из основной памяти, может не увидеть последних изменений поля. И наоборот, если поток на протяжении какого-то времени работает с регистрами и локальными кэшами, читая данные оттуда, он может сразу не увидеть изменений, сделанных другим потоком в основную память.
С точки зрения Java все переменные (за исключением локальных переменных, объявленных внутри метода) хранятся в главной памяти, которая доступна всем потокам, кроме этого, каждый поток имеет локальную—рабочую—память, где он хранит копии переменных, с которыми он работает, и при выполнении программы поток работает только с этими копиями. Надо отметить, что это описание не требование к реализации, а всего лишь модель, которая объясняет поведение программы, так, в качестве локальной памяти не обязательно выступает кэш память, это могут быть регистры процессора или потоки могут вообще не иметь локальной памяти.
При входе в synchronized метод или блок поток обновляет содержимое локальной памяти, а при выходе из synchronized метода или блока поток записывает изменения, сделанные в локальной памяти, в главную. Такое поведение synchronized методов и блоков следует из правил для отношения «происходит раньше»: так как все операции с памятью происходят раньше освобождения монитора и освобождение монитора происходит раньше захвата монитора, то все операции с памятью, которые были сделаны потоком до выхода из synchronized блока должны быть видны любому потоку, который входит в synchronized блок для того же самого монитора. Очень важно, что это правило работает только в том случае, если потоки синхронизируются, используя один и тот же монитор!
Что касается volatile переменных, то запись таких переменных производится в основную память, минуя локальную. и чтение volatile переменной производится также из основной памяти, то есть значение переменной не может сохраняться в регистрах или локальной памяти потока и операция чтения этой переменной гарантированно вернёт последнее записанное в неё значение.
Есть одна проблема, связанная с final полями: реализация разрешает менять значения таких полей после создания объекта (это может быть сделано, например, с использованием механизма reflection). Если значение final поля—константа, чьё значение известно на момент компиляции, изменения такого поля могут не иметь эффекта, так-как обращения к этой переменной могли быть заменены компилятором на константу. Также спецификация разрешает другие оптимизации, связанные с final полями, например, операции чтения final переменной могут быть переупорядочены с операциями, которые потенциально могут изменить такую переменную. Так что рекомендуется изменять final поля объекта только внутри конструктора, в противном случае поведение не специфицировано.
Reordering (переупорядочивание). Для увеличения производительности процессор/компилятор могут переставлять местами некоторые инструкции/операции. Вернее, с точки зрения потока, наблюдающего за выполнением операций в другом потоке, операции могут быть выполнены не в том порядке, в котором они идут в исходном коде. Тот же эффект может наблюдаться, когда один поток кладет результаты первой операции в регистр или локальный кэш, а результат второй операции попадает непосредственно в основную память. Тогда второй поток, обращаясь к основной памяти может сначала увидеть результат второй операции, и только потом первой, когда все регистры или кэши синхронизируются с основной памятью. Еще одна причина reordering, может заключаться в том, что процессор может решить поменять порядок выполнения операций, если, например, сочтет что такая последовательность выполнится быстрее.
Вопрос reordering также регулируется набором правил для отношения «происходит раньше» и у этих правил есть следствие, касающееся порядка операций, используемое на практике: операции чтения и записи volatile переменных не могут быть переупорядочены с операциями чтения и записи других volatile и не- volatile переменных. Это следствие делает возможным использование volatile переменной как флага, сигнализирующем об окончании какого-либо действия. В остальном правила, касающиеся порядка выполнения операций, гарантируют упорядоченность операций для конкретного набора случаев (таких как, например, захват и освобождение монитора), во всех остальных случаях оставляя компилятору и процессору полную свободу для оптимизаций.
Что такое «потокобезопасность»?
Потокобезопасность – свойство объекта или кода, которое гарантирует, что при исполнении или использовании несколькими потоками, код будет вести себя, как предполагается. Например потокобезопасный счётчик не пропустит ни один счёт, даже если один и тот же экземпляр этого счётчика будет использоваться несколькими потоками.
В чём разница между «конкуренцией» и «параллелизмом»?
Конкуренция — это способ одновременного решения множества задач.
Параллелизм — это способ выполнения разных частей одной задачи.
Что такое «кооперативная многозадачность»? Какой тип многозадачности использует Java? Чем обусловлен этот выбор?
Java использует вытесняющую многозадачность, при которой решение о переключении между потоками процесса принимает операционная система.
В отличие от кооперативной многозадачности управление операционной системе передаётся вне зависимости от состояния работающих приложений, благодаря чему, отдельные зависшие потоки процесса, как правило, не «подвешивают» всю систему целиком. За счёт регулярного переключения между задачами также улучшается отзывчивость приложения и повышается оперативность освобождения ресурсов, которые больше не используются.
В реализации вытесняющая многозадачность отличается от кооперативной, в частности, тем, что требует обработки системного прерывания от аппаратного таймера.
Что такое ordering, as-if-serial semantics, sequential consistency, visibility, atomicity, happens-before, mutual exclusion, safe publication?
ordering механизм, который определяет, когда один поток может увидеть out-of-order (неверный) порядок исполнения инструкций другого потока. CPU для повышения производительности может переупорядочивать процессорные инструкции и выполнять их в произвольном порядке до тех пор пока для потока внутри не будет видно никаких отличий. Гарантия, предоставляемая этим механизмом, называется as-if-serial semantics.
visibility определяет, когда действия в одном потоке становятся видны из другого потока.
atomicity — атомарность операций. Атомарная операция выглядит единой и неделимой командой процессора, которая может быть или уже выполненной или ещё невыполненной.
Чем отличается процесс от потока?
Процесс — экземпляр программы во время выполнения, независимый объект, которому выделены системные ресурсы (например, процессорное время и память). Каждый процесс выполняется в отдельном адресном пространстве: один процесс не может получить доступ к переменным и структурам данных другого. Если процесс хочет получить доступ к чужим ресурсам, необходимо использовать межпроцессное взаимодействие. Это могут быть конвейеры, файлы, каналы связи между компьютерами и многое другое.
Для каждого процесса ОС создает так называемое «виртуальное адресное пространство», к которому процесс имеет прямой доступ. Это пространство принадлежит процессу, содержит только его данные и находится в полном его распоряжении. Операционная система же отвечает за то, как виртуальное пространство процесса проецируется на физическую память.
Поток(thread) — определенный способ выполнения процесса, определяющий последовательность исполнения кода в процессе. Потоки всегда создаются в контексте какого-либо процесса, и вся их жизнь проходит только в его границах. Потоки могут исполнять один и тот же код и манипулировать одними и теми же данными, а также совместно использовать описатели объектов ядра, поскольку таблица описателей создается не в отдельных потоках, а в процессах. Так как потоки расходуют существенно меньше ресурсов, чем процессы, в процессе выполнения работы выгоднее создавать дополнительные потоки и избегать создания новых процессов.
Что такое «зелёные потоки» и есть ли они в Java?
Виртуальная машина Java берёт на себя заботу о переключении между разными green threads, а сама машина работает как один поток ОС. Это даёт несколько преимуществ. Потоки ОС относительно дороги в большинстве POSIX-систем. Кроме того, переключение между native threads гораздо медленнее, чем между green threads.
Это всё означает, что в некоторых ситуациях green threads гораздо выгоднее, чем native threads. Система может поддерживать гораздо большее количество green threads, чем потоков OС. Например, гораздо практичнее запускать новый green thread для нового HTTP-соединения к веб-серверу, вместо создания нового native thread.
Однако есть и недостатки. Самый большой заключается в том, что вы не можете исполнять два потока одновременно. Поскольку существует только один native thread, только он и вызывается планировщиком ОС. Даже если у вас несколько процессоров и несколько green threads, только один процессор может вызывать green thread. И всё потому, что с точки зрения планировщика заданий ОС всё это выглядит одним потоком.
Начиная с версии 1.2 Java поддерживает native threads, и с тех пор они используются по умолчанию.
Каким образом можно создать поток?
Помимо того, что Runnable помогает разрешить проблему множественного наследования, несомненный плюс от его использования состоит в том, что он позволяет логически отделить логику выполнения задачи от непосредственного управления потоком.
Как принудительно запустить поток?
Никак. В Java не существует абсолютно никакого способа принудительного запуска потока. Это контролируется JVM и Java не предоставляет никакого API для управления этим процессом.
Что такое «монитор» в Java?
Монитор, мьютекс (mutex) – это средство обеспечения контроля за доступом к ресурсу. У монитора может быть максимум один владелец в каждый текущий момент времени. Следовательно, если кто-то использует ресурс и захватил монитор для обеспечения единоличного доступа, то другой, желающий использовать тот же ресурс, должен подождать освобождения монитора, захватить его и только потом начать использовать ресурс.
Удобно представлять монитор как id захватившего его объекта. Если этот id равен 0 – ресурс свободен. Если не 0 – ресурс занят. Можно встать в очередь и ждать его освобождения.
Дайте определение понятию «синхронизация».
В Java все объекты имеют одну блокировку, благодаря которой только один поток одновременно может получить доступ к критическому коду в объекте. Такая синхронизация помогает предотвратить повреждение состояния объекта. Если поток получил блокировку, ни один другой поток не может войти в синхронизированный код, пока блокировка не будет снята. Когда поток, владеющий блокировкой, выходит из синхронизированного кода, блокировка снимается. Теперь другой поток может получить блокировку объекта и выполнить синхронизированный код. Если поток пытается получить блокировку объекта, когда другой поток владеет блокировкой, поток переходит в состояние Блокировки до тех пор, пока блокировка не снимется.
Какие существуют способы синхронизации в Java?
В каких состояниях может находиться поток?
Потоки могут находиться в одном из следующих состояний:
Можно ли создавать новые экземпляры класса, пока выполняется static synchronized метод?
Да, можно создавать новые экземпляры класса, так как статические поля не принадлежат к экземплярам класса.
Зачем может быть нужен private мьютекс?
Эти методы определены у класса Object и предназначены для взаимодействия потоков между собой при межпоточной синхронизации.
Почему методы wait() и notify() вызываются только в синхронизированном блоке?
Чем отличается работа метода wait() с параметром и без параметра?
Метод yield() служит причиной того, что поток переходит из состояния работающий (running) в состояние работоспособный (runnable), давая возможность другим потокам активизироваться. Но следующий выбранный для запуска поток может и не быть другим.
Метод sleep() вызывает засыпание текущего потока на заданное время, состояние изменяется с работающий (running) на ожидающий (waiting).
Когда поток вызывает join() для другого потока, текущий работающий поток будет ждать, пока другой поток, к которому он присоединяется, не будет завершён:
Простейший способ избежать взаимной блокировки – не допускать цикличного ожидания. Этого можно достичь, получая мониторы разделяемых ресурсов в определённом порядке и освобождая их в обратном порядке.
livelock – тип взаимной блокировки, при котором несколько потоков выполняют бесполезную работу, попадая в зацикленность при попытке получения каких-либо ресурсов. При этом их состояния постоянно изменяются в зависимости друг от друга. Фактической ошибки не возникает, но КПД системы падает до 0. Часто возникает в результате попыток предотвращения deadlock.
Реальный пример livelock, – когда два человека встречаются в узком коридоре и каждый, пытаясь быть вежливым, отходит в сторону, и так они бесконечно двигаются из стороны в сторону, абсолютно не продвигаясь в нужном им направлении.
Как проверить, удерживает ли поток монитор определённого ресурса?
На каком объекте происходит синхронизация при вызове static synchronized метода?
В чём различия между volatile и Atomic переменными?
Что значит «приоритет потока»?
Приоритеты потоков используются планировщиком потоков для принятия решений о том, когда какому из потоков будет разрешено работать. Теоретически высокоприоритетные потоки получают больше времени процессора, чем низкоприоритетные. Практически объем времени процессора, который получает поток, часто зависит от нескольких факторов помимо его приоритета.
Можно ли сделать основной поток программы демоном?
Нет. Потоки-демоны позволяют описывать фоновые процессы, которые нужны только для обслуживания основных потоков выполнения и не могут существовать без них.
Что значит «усыпить» поток?
CountDownLatch (замок с обратным отсчетом) предоставляет возможность любому количеству потоков в блоке кода ожидать до тех пор, пока не завершится определенное количество операций, выполняющихся в других потоках, перед тем как они будут «отпущены», чтобы продолжить свою деятельность. В конструктор CountDownLatch(int count) обязательно передается количество операций, которое должно быть выполнено, чтобы замок «отпустил» заблокированные потоки.
Примером CountDownLatch из жизни может служить сбор экскурсионной группы: пока не наберется определенное количество человек, экскурсия не начнется.
CyclicBarrier реализует шаблон синхронизации «Барьер». Циклический барьер является точкой синхронизации, в которой указанное количество параллельных потоков встречается и блокируется. Как только все потоки прибыли, выполняется опционное действие (или не выполняется, если барьер был инициализирован без него), и, после того, как оно выполнено, барьер ломается и ожидающие потоки «освобождаются». В конструкторы барьера CyclicBarrier(int parties) и CyclicBarrier(int parties, Runnable barrierAction) обязательно передается количество сторон, которые должны «встретиться», и, опционально, действие, которое должно произойти, когда стороны встретились, но перед тем когда они будут «отпущены».
Что такое race condition?
Существует ли способ решения проблемы race condition?
Распространённые способы решения:
Очевидных способов выявления и исправления состояний гонки не существует. Лучший способ избавиться от гонок — правильное проектирование многозадачной системы.
Как остановить поток?
Схема действия при этом получается следующей:
Что происходит, когда в потоке выбрасывается исключение?
Механизм прерывания работы потока в Java реализован с использованием внутреннего флага, известного как статус прерывания. Прерывание потока вызовом Thread.interrupt() устанавливает этот флаг. Методы Thread.interrupted() и isInterrupted() позволяют проверить, является ли поток прерванным.
Нестатический метод isInterrupted() используется одним потоком для проверки статуса прерывания у другого потока, не изменяя флаг прерывания.
Что такое «пул потоков»?
Создание потока является затратной по времени и ресурсам операцией. Количество потоков, которое может быть запущено в рамках одного процесса также ограниченно. Чтобы избежать этих проблем и в целом управлять множеством потоков более эффективно в Java был реализован механизм пула потоков (thread pool), который создаётся во время запуска приложения и в дальнейшем потоки для обработки запросов берутся и переиспользуются уже из него. Таким образом, появляется возможность не терять потоки, сбалансировать приложение по количеству потоков и частоте их создания.
Методы Executors для создания пулов:
Какого размера должен быть пул потоков?
Настраивая размер пула потоков, важно избежать двух ошибок: слишком мало потоков (очередь на выполнение будет расти, потребляя много памяти) или слишком много потоков (замедление работы всей систему из-за частых переключений контекста).
Использование процессора – не единственный фактор, важный при настройке размера пула потоков. По мере возрастания пула потоков, можно столкнуться с ограничениями планировщика, доступной памяти, или других системных ресурсов, таких, как количество сокетов, дескрипторы открытого файла, или каналы связи базы данных.
Что будет, если очередь пула потоков уже заполнена, но подаётся новая задача?
В чём заключается различие между методами submit() и execute() у пула потоков?
Оба метода являются способами подачи задачи в пул потоков, но между ними есть небольшая разница.
execute(Runnable command) определён в интерфейсе Executor и выполняет поданную задачу и ничего не возвращает.
В чем заключаются различия между cтеком (stack) и кучей (heap) с точки зрения многопоточности?
Cтек – участок памяти, тесно связанный с потоками. У каждого потока есть свой стек, которые хранит локальные переменные, параметры методов и стек вызовов. Переменная, хранящаяся в стеке одного потока, не видна для другого.
Как поделиться данными между двумя потоками?
Какой параметр запуска JVM используется для контроля размера стека потока?
Как получить дамп потока?
Среды исполнения Java на основе HotSpot генерируют только дамп в формате HPROF. В распоряжении разработчика имеется несколько интерактивных методов генерации дампов и один метод генерации дампов на основе событий.
Метод на основе событий:
Что такое ThreadLocal-переменная?
Следует обратить внимание, что ThreadLocal изолирует именно ссылки на объекты, а не сами объекты. Если изолированные внутри потоков ссылки ведут на один и тот же объект, то возможны коллизии.
Что понимается под блокировкой с повторным входом (reentrant)? Просто то, что есть подсчет сбора данных, связанный с блокировкой, и если поток, который удерживает блокировку, снова ее получает, данные отражают увеличение, и тогда для реального разблокирования нужно два раза снять блокировку. Это аналогично семантике synchronized; если поток входит в синхронный блок, защищенный монитором, который уже принадлежит потоку, потоку будет разрешено дальнейшее функционирование, и блокировка не будет снята, когда поток выйдет из второго (или последующего) блока synchronized, она будет снята только когда он выйдет из первого блока synchronized, в который он вошел под защитой монитора.
Резюмируя, можно сказать, что когда состязания за блокировку нет либо оно очень мало, то synchronized возможно будет быстрее. Если присутствует заметное состязание за доступ к ресурсу, то скорее всего ReentrantLock даст некое преимущество.
Что такое «блокирующий метод»?
Блокирующий метод – метод, который блокируется, до тех пор, пока задание не выполнится, например метод accept() у ServerSocket блокируется в ожидании подключения клиента. Здесь блокирование означает, что контроль не вернётся к вызывающему методу до тех пор, пока не выполнится задание. Так же существуют асинхронные или неблокирующиеся методы, которые могут завершится до выполнения задачи.
Что такое «фреймворк Fork/Join»?
Решение всех подзадач (в т.ч. и само разбиение на подзадачи) происходит параллельно.
Для решения некоторых задач этап Join не требуется. Например, для параллельного QuickSort — массив рекурсивно делится на всё меньшие и меньшие диапазоны, пока не вырождается в тривиальный случай из 1 элемента. Хотя в некотором смысле Join будет необходим и тут, т.к. всё равно остаётся необходимость дождаться пока не закончится выполнение всех подзадач.
Ещё одно замечательное преимущество этого фреймворка заключается в том, что он использует work-stealing алгоритм: потоки, которые завершили выполнение собственных подзадач, могут «украсть» подзадачи у других потоков, которые всё ещё заняты.
Semaphore – это новый тип синхронизатора: семафор со счётчиком, реализующий шаблон синхронизации Семафор. Доступ управляется с помощью счётчика: изначальное значение счётчика задаётся в конструкторе при создании синхронизатора, когда поток заходит в заданный блок кода, то значение счётчика уменьшается на единицу, когда поток его покидает, то увеличивается. Если значение счётчика равно нулю, то текущий поток блокируется, пока кто-нибудь не выйдет из защищаемого блока. Semaphore используется для защиты дорогих ресурсов, которые доступны в ограниченном количестве, например подключение к базе данных в пуле.
Что такое double checked locking Singleton?
Следует заметить, что требование volatile обязательно. Проблема Double Checked Lock заключается в модели памяти Java, точнее в порядке создания объектов, когда возможна ситуация, при которой другой поток может получить и начать использовать (на основании условия, что указатель не нулевой) не полностью сконструированный объект. Хотя эта проблема была частично решена в JDK 1.5, однако рекомендация использовать volatile для Double Cheсked Lock остаётся в силе.
Как создать потокобезопасный Singleton?
Чем полезны неизменяемые объекты?
Что такое busy spin?
Перечислите принципы, которым вы следуете в многопоточном программировании?
При написании многопоточных программ следует придерживаться определённых правил, которые помогают обеспечить достойную производительность приложения в сочетании с удобной отладкой и простотой дальнейшей поддержки кода.
Какое из следующих утверждений о потоках неверно?
Вызов метода start() дважды для одного и того же объекта Thread приведёт к генерированию исключения IllegalThreadStateException во время выполнения, следовательно, утверждение 1 верно. Утверждение 2 верно, так как порядок, в котором выполняются потоки, определяется Планировщиком потоков, независимо от того, какой поток запущен первым. Утверждение 4 верно, так как поток не освободит блокировки, которые он держит, когда он переходит в состояние Ожидания.
Даны 3 потока Т1, Т2 и Т3? Как реализовать выполнение в последовательности Т1, Т2, Т3?
Напишите минимальный неблокирующий стек (всего два метода — push() и pop() ).
