Вы достигли нового уровня

Уровень 25

1. Enum, все особенности

- Привет, Амиго!

- Привет, Билаабо!

- Я расскажу тебе об очень интересной штуке. Она довольно простая, но от этого становится еще интереснее. Эта штука называется enum. enum – это тип, заданный перечислением набора значений, которые могут принимать его переменные. Сразу пример:

Определение класса-enum
public enum Direction
{
 UP,
 DOWN,
 LEFT,
 RIGHT,
}
Использование enum’а
Direction direction = Direction.LEFT;

if (direction == Direction.LEFT)
 direction = Direction.RIGHT;
else
 direction = Direction.DOWN;

- Т.е. мы просто перечисляем набор значений и все?

- Да, объявляем enum, внутри через запятую пишем его возможные значения.

Использовать его тоже довольно просто.

- А ему можно присваивать null?

- Да, enum – это обычный класс – вернее, одна из его разновидностей, таких как interface.

- Т.е. везде, где я могу использовать класс, я могу использовать enum?

- Да.

- А внутри класса можно объявить enum?

- Да.

- А унаследоваться от enum?

- Нет, ни наследоваться от enum, ни наследовать enum нельзя.

- А почему?

- Потому что Java-компилятор превращает enum примерно вот в это:

 
public final class Direction extends Enum
{
 public static final Direction UP = new Direction();
 public static final Direction DOWN = new Direction();
 public static final Direction LEFT = new Direction();
 public static final Direction RIGHT = new Direction();

 private Direction() {} //приватный конструктор
}

Как видно из этого примера:

1) Класс Direction обязательно наследуется от класса Enum, поэтому его нельзя унаследовать больше ни от чего.

2) Класс Direction объявлен final, поэтому от него нельзя ничего унаследовать.

3) Набор значений класса Direction на самом деле – это public static final переменные типа Direction. Что видно по коду их использования:

 
Direction direction = Direction.LEFT;

4) Класс Direction содержит только один конструктор и тот – приватный. Это значит, что объекты класса Direction можно создать только в коде внутри класса. Кроме объявленных объектов, других объектов создать нельзя.

5) Переменным типа Direction можно присвоить ссылку на любой из существующих объектов типа Direction – все они определены у него внутри. Других объектов данного типа нет, и не будет.

6) Объекты типа Direction можно сравнивать с помощью знака «==» - это будет просто сравнение ссылок.

- Не сказал бы, что все очень понятно, но после твоего примера, стало гораздо понятнее.

- Отлично. Тогда вот тебе еще чуток информации:

1) У каждого объекта типа Direction есть его уникальный номер. У первого (UP) – 0, у второго (DOWN) – 1, у третьего (LEFT) – 2, и т.д. Получить этот номер можно с помощью метода ordinal(). Внимание на экран:

 
Direction direction = Direction.LEFT;
int index = direction.ordinal();
int index2 = Direction.RIGHT.ordinal();

2) У каждого enum’а есть метод values(), который возвращает массив значений enum’а.

 
int leftIndex = Direction.LEFT.ordinal();

Direction[] array = Direction.values();
Direction left = array[leftIndex];

Т.е. мы можем у любого элемента enum’а получить его номер, а затем по номеру опять получить элемент.

Мы также можем использовать enum в цикле foreach:

 
for (Direction direction : Direction.values())
{
 System.out.println(direction);
}
Вывод на экран будет:
UP
DOWN
LEFT
RIGHT

- Значит, у enum’а переопределен метод String? Он же не выводит на экран что-то вроде
«com.javarush.Direction@123fd4»?

- Да, более того у каждого enum’а, а значит и у Direction есть возможность преобразовать его в строку и обратно.

Преобразование в строку:
String left = Direction.LEFT.toString(); // left == "LEFT";
Преобразование строки в enum:
Direction direction = Direction.valueOf("LEFT");

- О, все понятно.

А что будет, если в функцию valueOf передать строку, которой нет в Direction? Например, "AMIGO"?

- А ты сам как думаешь?

- Exception?

- Ага. IllegalArgumentException.

На этом наше введение в мир enum’ов окончено.

2. Задачи на enum

- Привет, Амиго!

Билаабо придумал для тебя задачи. Пользуйся, друг:

Задачи
1. Новые возможности!

Используя возможности enum-а, реализуйте метод getLetterPosition, который должен возвращать позицию буквы (A - 1).
Не должно быть закоментированного кода.
2. Машину на СТО не повезем!

Инициализируйте поле wheels, используя данные из loadWheelNamesFromDB.
Обработайте некорректные данные.

Подсказка: если что-то не то с колесами, то это не машина!

Сигнатуры не менять.
3. Свой enum

Реализуйте интерфейс Columnable у Column, описание методов смотрите в джавадоках.
Реализуйте логику метода Column.getVisibleColumns.
Создавать дополнительные поля нельзя.
Метод main не участвует в тестировании.

3. Thread Life Cyrcle, состояние объекта Thread

- Привет, Амиго!

Мы начинаем новую тему – работа с нитями или как их еще называют – трэдами (от thread).

- А я слышал, что треды называют потоками.

- Да, такое название все еще употребляется, но постепенно выходит из моды. Проблема в том, что есть еще stream, который дословно переводится как «поток». Поэтому thread’ы принято называть нитями (дословный перевод) или программными потоками. В противовес stream’ам, которые называют потоками ввода-вывода.

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

Сколько состояний ты можешь назвать прямо сейчас, Амиго?

- Два. Первое – это нить до вызова метода start(): объект есть, но нить еще не активна. И второе - после вызова метода start() – когда нить что-то делает, важное.

- Ты прав, такое разграничение есть, эти состояния называются new и running, но это только самое начало.

Во-первых, нить когда-нибудь заканчивает работу, а значит, может быть такая ситуация – объект Thread есть, но нить не в состоянии new и не в состоянии running. Такое состояние, когда нить завершила работу, называется terminated.

Но и это еще не все. Не стоит забывать, что в каждый момент времени работает только одна нить. А видимая одновременная работа – это постоянное перескакивание процессора с нити на нить. Для времени, когда нить как бы работает, а на самом деле ждет своей очереди, тоже есть отдельное состояние. Оно называется ready-to-run. Нить во время работы постоянно меняет состояние с running на ready и потом снова на running, когда становится активной.

Сразу после вызова метода start() нить получает статус ready-to-run, и помещается в общий список нитей, между которыми переключается Java-машина.

- Не так уж и сложно. До начала работы – состояние new, после окончание – terminated. А в процессе работы нить находится то в активном (running), то в пассивном (ready) режиме работы.

- Твоя краткость удивляет, но так и есть.

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

Вот как выглядит эта ситуация с состояниями:

Но и это еще не все. Есть еще отдельное состояние, когда нить не blocked, но и не ready – это waiting. Например, при вызове методов join() у другой нити.

При вызове join() у объекта другой нити, наша нить как-бы «присоединяется к ней», а на деле – просто ждет ее завершения.

Кроме того, есть еще метод wait(), (из набора wait, notify, notifyAll), вызов которого тоже переводит нить в состояние waiting.

- Ничего себе.

- Подожди! Но и это еще не все. Нить может спать, например, при вызове метода sleep. Для этого тоже есть отдельное состояние «timed waiting». «timed waiting» значит, что нить чего-то ждет ограниченное время. Если вызвать метод wait с параметром - wait(timeout) или join(timeout), то нить перейдет в состояние timed waiting.

Так что вот тебе полная схема:

- Гм. Это все? Или там есть еще 10 новых интересных состояний?

- Пока – все.

На самом деле – можешь запомнить только первую схему – она проще. Но вторая точнее.

Как ни странно – в интернете очень много схем состояний Thread и они все разные.

Поэтому я и привела тебе эту схему – она самая полная и правильная.

На этой схеме состояния ready и running объединены в единый блок – runnable, и знаешь почему?

- Нет. Я же в первый раз это все вообще вижу.

- У класса Thread есть внутренний класс State, а также метод public State getState().

Пример
public enum State
{
 NEW,
 RUNNABLE,
 BLOCKED,
 WAITING,
 TIMED_WAITING,
 TERMINATED;
}

Ты всегда можешь вызвать у объекта типа Thread метод getState() и узнать его текущее состояние. И, конечно, оно будет одним из значений enum State.

- Ясно. То есть настоящие состояния внутри java-машины, а есть состояния, которые можно получить из Java-кода с помощью метода State getState().

А в каких ситуациях я буду это использовать?

- Скорее всего – в никаких.

Но знать о том, что происходит при работе внутри нити, ты обязан. Иначе на твоем пути будет много ошибок, и ты даже не будешь догадываться об их причине.

Кроме того, состояния Thread очень любят спрашивать на собеседованиях.

4. Все нюансы создания и запуска новой нити, демоны

- Вот тебе новая и интересная тема.

Оказывается, нити можно запускать в различных режимах.

Обычная программа (состоящая из одной нити) заканчивает работу вместе с завершением работы своей главной нити. Главная нить заканчивает работать, и программа завершается, а java-машина освобождает ее память.

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

Так вот, в больших программах часто есть так называемые «служебные процессы» - это нити, работа которых состоит в обслуживании других частей программы. Сами по себе они не нужны. Например: процесс удаления неиспользуемых объектов (сборка мусора), логирование состояния памяти и ошибок, различные отчеты о текущем состоянии программы и т.д. и т.п.

Такие служебные процессы нужны, когда программа что-то делает, но не нужны сами по себе.

- Да, я это понял.

- Java позволяет запустить нить в "режиме демона" (daemon). Работа такой нити ничем не отличается от других, но если в программе все обычные нити завершили работу, но остались только нити-демоны - Java-машина завершит программу.

- Т.е. мы объявляем нить «служебной», и она просто не учитывается при закрытии программы и все?

- Э-э-э. Ну, ты и выдал – кратко и по существу. Собственно, я это и хотела тебе сказать.

- Краткость – сестра таланта. А талантливые роботы – талантливы во всем.

- Есть вопросы?

- Что нужно, чтобы запустить нить в режиме демона? Унаследоваться от какой-нибудь DaemonThread?

- Нет, все гораздо проще – у класса Thread есть метод setDaemon(boolean), надо передать в него true и все. Только вызвать его нужно до вызова метода start(), пока реальная нить не создана. Поменять тип уже запущенной нити невозможно.

Пример:

Пример
Thread thread = new LoggerThread();
thread.setDaemon(true);
thread.start();

- И это все?

- Да.

Хочу еще раз обратить твое внимание на процесс создания и запуска нити.

Когда мы создаем объект типа Thread, нить при этом еще не создается. Thread – это не нить, нить создаст сама Java-машина, когда будет вызван метод start(). А Thread - это специальный объект Java-машины, который позволяет получать информацию о нити и немного управлять ей.

- Ясно. Спасибо, Элли.

5. Задачи на тему "текущее состояне нити"

- Привет, Амиго! Как настроение?

- Отличное. Сегодня столько нового узнал. Особенно про состояния нитей.

- Да, отлично. Вот и проверим твои знания:

Задачи
1. Switch для нитей

Обработайте список нитей в зависимости от состояния:
1. Если нить еще не запущена, то запустите ее.
2. Если нить в ожидании, то прервите ее.
3. Если нить работает, то проверить маркер isInterrupted.
4. Если нить прекратила работу, то выведите в консоль ее приоритет. Используйте switch.
2. Без дураков

1. Создай private class MyUncaughtExceptionHandler, который на перехват исключения должен подождать половину секунды,
а затем вывести на экран secretKey, имя трэда и сообщение возникшего исключения.
Используй String.format(...).

Пример: super secret key, Thread-0, it's an example

2. Разберитесь в последовательности выполняемого кода и обеспечьте логирование возникновения исключения в п.1.
3. Метод main в тестировании не участвует.
3. Мониторинг состояния нити

В отдельном классе создать нить LoggingStateThread, которая будет выводить в консоль все изменения состояния (State) переданной в конструктор нити.
Нить LoggingStateThread должна сама завершаться после остановки переданной в конструктор нити.
Метод main не участвует в тестировании.

6. Все нюансы прерывания/остановки нитей

- Привет, Амиго!

Все новое – хорошо забытое старое. Сегодня я буду рассказывать про остановку нитей. Надеюсь, ты уже забыл, как работает метод interrupt().

- Да, Элли, полностью забыл.

- Отлично. Тогда напоминаю.

В Java, если кто-то хочет остановить работающую нить, у него есть возможность подать нити об этом сигнал. Для этого нужно установить скрытую переменную нити isInterrupted в true.

У каждой нити (у класса Thread) есть метод interrupt(), который используется для установки такого флага. При вызове метода interrupt() переменная isInterrupted внутри нити устанавливается равной true.

И когда нить вызывает методы Thread.sleep() или метод join(), в этих методах происходит скрытая проверка – а не выставлен ли у нашей текущей нити флаг isInterrupted. Если этот флаг выставлен (переменная isInterrupted равно true), то методы выбрасывают исключение InterruptedException.

Вот, напомню тебе старый пример:

КодОписание
class Clock implements Runnable
{
 public void run()
 {
  Thread current = Thread.currentThread();

  while (!current.isInterrupted())
  {
   Thread.sleep(1000);
   System.out.println("Tik");

  }
 }
}
Объект Clock в своем методе run получает объект текущей его нити.

Класс Clock (часы) будет писать в консоль раз в секунду слово «Tik», пока переменная isInterrupt текущей нити равна false.

Когда переменная isInterrupt станет равной true, метод run завершится.
public static void main(String[] args)
{
 Clock clock = new Clock();
 Thread clockThread = new Thread(clock);
 clockThread.start();


 Thread.sleep(10000);
 clockThread.
interrupt();
}
Главная нить, запускает дочернюю нить – часы, которая должна работать вечно.

Ждет 10 секунд и отменяет задание, вызовом метода interrupt.

Главная нить завершает свою работу.

Нить часов завершает свою работу.

Тут мы используем метод sleep для организации вечного цикла в методе run. В цикле есть автоматическая проверка переменной isInterrupt. Если нить вызовет метод sleep, то этот метод сначала проверит, а не установлена ли для текущей (вызвавшей его нити) переменная isInterrupt в true. И если установлена, то метод не будет спать, а выкинет исключение InterruptedException.

- Но в этом примере мы постоянно проверяем переменную isInterrupted в условии цикла.

Я помню, были какие-то причины, по которым мы не могли использовать такой подход. Не напомнишь?

- Во-первых, не всегда в методе run есть цикл. Метод может состоять просто из двух десятков вызовов других методов. Тогда перед вызовом каждого придется добавлять проверку isInterrupted.

Во-вторых, вдруг какой-то метод очень долго исполняется, т.к. делает много разных действий.

В-третьих, выкидывание исключения – это не замена проверке isInterrupted, а скорее удобное дополнение. Выкинутое исключение позволяет быстро раскрутить стек вызовов до самого run.

В-четвертых, метод sleep часто используют, и, получается, к такому полезному методу неявно добавили не менее полезную проверку. Вроде бы никто специально проверку не добавлял, а она есть. Это очень ценно, когда ты используешь много чужого кода и не можешь сам добавить в него проверку.

В-пятых, дополнительная проверка не приводит к снижению производительности. Вызов метода sleep значит, что нить должна ничего не делать (спать), поэтому дополнительная работа никому не мешает.

- Точно, именно это ты и говорила тогда.

А что насчет твоей фразы «Никто не гарантирует, что нить можно остановить. Она может остановиться только сама». Можешь ее растолковать?

- Конечно.

Раньше, в первых версиях Java, у нитей был метод stop(). И при его вызове Java-машина действительно останавливала нить. Но потом оказалось, что если нить, которую прерывали таким образом, делала что-то за пределами Java-машины и (например писала в файл или вызывала функции ОС), то прерывание такой нити приводила к большому количеству проблем – незакрытые файлы, неосвобожденные занятые системные ресурсы и т.д.

На общем совещании проектировщиков Java было решено убрать метод принудительной остановки у нитей. Теперь мы всего лишь можем установить определенный флаг (isInterrupted) и надеяться, что код нити был написан правильно, и этот флаг будет обработан. Этот флаг – это как плакат с надписью – «нить, остановить, пожалуйста, очень надо!». Но остановится она или нет – это ее дело.

- А как же InterruptedException?

- А если внутри кода, который работает в этой нити, есть куча try-catch блоков? Даже если InterruptedException где-нибудь да и выскочит, абсолютно не факт, что какой-то try-catch не захватит его и не забудет о нем. Так что никаких гарантий остановки нити нет.

Другое дело, что нити уже считают достаточно низкоуровневым программированием. Но об этом я расскажу тебе в следующий раз.

- Прямо не Элли, а Шахеризада!

- Так, Амиго! Все понятно по текущей лекции?

- Ага.

- Вот и отлично.

7. Задачи на прерывание нитей

- Как раз тебя жду – тут столько интересной работы:

Задачи
1. Работать в поте лица!

Реализуйте логику метода interrupt, который должен прерывать трэд предварительно закрыв используемые ресурсы
Используйте метод super-класса в блоке finally
2. Не валять дурака

Восстановите логику класса TaskManipulator.
3. Все не так легко, как кажется

1. Почитать в инете про Socket, ThreadPoolExecutor, RunnableFuture, Callable
2. Реализуйте логику метода cancel в классе SocketTask
3. Реализуйте логику метода cancel для локального класса внутри метода newTask в классе SocketTask

8. Стек вызовов и перехват ошибок

- Привет, Амиго!

Хочу рассказать тебе меленькую, но интересную вещь.

- Я слушаю. Люблю интересные и маленькие вещи.

Так вот, ты знаешь, что у каждого объекта Thread есть метод run(). И что можно запустить его на выполнение в отдельной нити с помощью метода start().

- Да, конечно.

- А вот представь теперь ситуацию – ты запускаешь нить, чтобы она выполнила нужную тебе работу, а у нее внутри возникает Exception и она завершает работу, так и не выполнив того, что надо. Тебе бы надо как-то узнать об этой ошибке?

- Согласен. А что, как-то можно перехватить исключение, которое возникло в другой запущенной нити. Неужели даже это есть в Java?

- Обижаешь. Еще как есть.

Разработчики Java придумали специальный интерфейс UncaughtExceptionHandler. Вот как можно перехватить и обработать исключение в другой нити, если она не перехватила его сама:

Пример
public class DownloadManager
{
 public static void main(String[] args)
 {
   Thread thread = new DownloadThread();
   thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler()
   {
    @Override
    public void uncaughtException(Thread t, Throwable e)
    {

    }
 });


 thread.start();
}

У объекта типа Thread есть специальный метод – setUncaughtExceptionHandler. В него нужно предать объект, который реализует интерфейс Thread.UncaughtExceptionHandler. У этого интерфейса есть всего один метод uncaughtException(Thread t, Throwable e). Именно этот метод будет вызван у переданного объекта, если внутри метода run возникнет исключение, которое не будет захвачено.

В приведенном мной примере, я просто объявляю анонимный внутренний класс (выделено красным), реализующий интерфейс Thread.UncaughtExceptionHandler. и переопределяю в нем метод uncaughtException(Thread t, Throwable e).

Как видно из параметров метода, в него будут переданы два параметра – ссылка на Thread, в котором возникло исключение, и само исключение – Throwable e.

- А зачем нужна ссылка на Thread - переменная t? Мы же знаем, в какой поток устанавливаем объект типа Thread.UncaughtExceptionHandler?

- А это сделано для того, чтобы ты мог написать универсальный обработчик таких ситуация – создать один единственный объект и передавать его десяткам различных потоков. Тогда внутри метода uncaughtException(Thread t, Throwable e) у тебя всегда будет ссылка на нить, где возникло исключение.

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

- Я тебе верю. Ты еще ни разу не ошибалась.

9. Задачи на получение ошибок из нити

- А чего это у тебя все время аж по 3 задачи?

Как насчет решить еще 50?

- 50? О_о

- Давай лучше 3 как обычно.

Задачи
1. Поживем - увидим

Все исключения, которые возникают в процессе работы нити Solution, должны быть обработаны одним из вариантов:
1. Если это Error, то вывести в консоль "Нельзя дальше работать"
2. Если это Exception, то вывести в консоль "Надо обработать"
3. Если это Throwable, то вывести в консоль "ХЗ"
Реализуйте эту логику.
2. Вооружаемся до зубов!

Создайте свой UncaughtExceptionHandler в виде локального класса внутри конструктора.
UncaughtExceptionHandler должен маскать звездочками имя трэда.
"Thread-0" должно быть заменено на "********"
"Thread-4321" должно быть заменено на "***********"
3. Живем своим умом

В классе Solution реализуйте интерфейс UncaughtExceptionHandler, который должен:
1. прервать нить, которая бросила исключение.
2. вывести в консоль стек исключений начиная с самого вложенного.

Пример исключения:
new Exception("ABC", new RuntimeException("DEF", new IllegalAccessException("GHI")))

Пример вывода:
java.lang.IllegalAccessException: GHI
java.lang.RuntimeException: DEF
java.lang.Exception: ABC

10. Другие методы класса Thread (sleep, yield...)

- Привет, Амиго!

А теперь немного расскажу про методы sleep, yield, join.

- Это скучно. Я тут нашел вопрос к собеседованию «Чем отличаются методы yield(), sleep(), wait()?». Может расскажешь?

- Не вопрос. Начну с того, что это три совершенно разных метода.

1) sleep(timeout) – останавливает текущую нить (в которой sleep был вызван) на timeout миллисекунд. Нить при этом переходит в состояние TIMED_WAITING. Метод может завершиться раньше, если был установлен флаг isInterrupted.

ПримерОписание
Thread.sleep(500); Текущая нить приостанавливает свою работу на 500 миллисекунд – 0.5 секунды.

2) yield() – текущая нить «пропускает свой ход». Нить из состояния running переходит в состояние ready, а Java-машина приступает к выполнению следующей нити. Состояния running & ready – это подсостояния состояния RUNNABLE.

ПримерОписание
Thread.yield(); Текущая нить «пропускает свой ход» - Java сразу переключается на выполнение следующей нити.

3) wait(timeout) – это одна из версий метода wait() – версия с таймаутом. Метод wait можно вызвать только внутри блока synchronized у объекта-мютекса, который был «залочен (заблокирован)» текущей нитью, в противном случае метод выкинет исключение IllegalMonitorStateException.

В результате вызова этого метода, блокировка с объекта-мютекса снимается, и он становится доступен для захвата и блокировки другой нитью. При этом нить переходит в состояние BLOCKED для метода wait() без параметров, но в состояние TIMED_WAITING для метода wait(timeout).

ПримерОписание
Object monitor = getMonitor();
synchronized(monitor)
{
 …
 monitor.wait(500);
 …
}
При вызове метода wait, текущая нить снимает блокировку с объекта monitor, и засыпает на 500 миллисекунд. Объект monitor может быть захвачен другой нитью.
Через 500 миллисекунд нить проснется и если monitor не был занят, то захватит его и продолжит работу.
Если монитор окажется занят другой нитью, текущая нить перейдет в состояние BLOCKED.

4) join(timeout)

Этого метода не было в твоем вопросе, но он есть в моих планах, так что расскажу и про него. При вызове метода join() или join(timeout) текущая нить как бы «присоединяется» к нити, у объекта которой был вызван данный метод. Текущая нить засыпает и ждет окончания нити, к которой она присоединилась (чей метод join() был вызван).

При этом текущая нить переходит в состояние WAITING для метода join и в состояние TIMED_WAITING для метода join(timeout).

ПримерОписание
Thread thread = getWorkThread();
thread.join(500);
Текущая нить присоединится к нити thread и будет ждать ее окончания.
Но отсоединится через 500 миллисекунд и продолжит работу.

timeout в методах wait(timeout) и join(timeout) значит, что метод засыпает, ждет чего-то, но не дольше чем timeout миллисекунд. После чего просыпается.

- Такое ощущение, что единственное, что есть общего у этих методов – это timeout. Но делают они совершенно разные вещи.

- Так оно и есть.

11. Задачи на yield, переуступку кванта времени

- Привет, Амиго! Тренируйся, вот тебе пара задач.

Задачи
1. Обеспечение отсутствия прерывания важной операции

Просмотрите метод moveMoney
Если RANDOM.nextInt(5000) больше порогового значения THRESHOLD_VALUE,
то обеспечьте переуступку кванта времени (переход хода для текущей нити).
Добавьте этот код в единственное допустимое место.
2. Первый закон Финэйгла: если эксперимент удался, что-то здесь не так...

Обеспечьте переуступку кванта времени (переход хода для текущей нити) для последовательных выводов текста в консоль

12. Учимся гуглить. Как запустить отдельный процесс

- Привет, Амиго! Вот несколько тем, которые я советую тебе сегодня погуглить:

 Найди ответы на следующие вопросы:
1 Что такое ThreadGroup и зачем он нужен?
2 Что такое ThreadPool и зачем он нужен?
3 Что такое ThreadPoolExecutor и зачем он нужен?
4 Что такое Concurrency?
5 Что такое «атомарные типы» в Java?
6 Зачем нужен класс ThreadLocal?
7 Что такое модификатор volatile?
8 Что такое Executor?
9 Что такое ExecutorService?
10 Зачем нужен ScheduledExecutorService?

13. Профессор дает доп. материал

- Привет, Амиго!

Вот тебе отличная лекция с дополнительным материалом:

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

14. Хулио

- Привет, Амиго! Никак не могу попасть на концерты Курта Кобейна и Виктора Цоя, вообще нигде не выступают! Как думаешь, есть у меня шанс?

- Всему свое время. Я бы на твоём месте не слишком пытался попасть на их концерт.

Оригинал видео на YouTube

15. Вопросы к собеседованию по этой теме

- Привет, Амиго!

 Вопросы к собеседованиям
1 Назовите все состояния объекта Thread?
2 В какие состояния может перейти нить, при входе в блок synchronized?
3 В какое состояние перейдет нить, при вызове метода wait()?
4 В какое состояние перейдет нить, при вызове метода wait(500)?
5 В какое состояние перейдет нить, при вызове метода notify()?
6 В какое состояние перейдет нить, при вызове метода notifyAll()?
7 Три нити в блоке synchronized вызвали wait() у объекта-мютекса.
В какое состояние перейдут эти нити, если четвертая нить вызовет notifyAll()?
8 Чем отличается join(500) от wait(500)?
9 Чем отличается wait(500) от sleep(500)?
10 В какое состояние перейдет нить при вызове метода yield()?

16. Большая задача

- Привет, Амиго!

- Привет!

- Ты как разговариваешь со старшим по званию?

- Виноват, сэр.

Здравия желаю, Капитан Бобров.

- То-то же.

У тебя сегодня новая секретная миссия.

Нужно написать симуляцию космической войны.

- Круто. То есть я хотел сказать, я - готов, сэр.

У меня как раз есть идея. Как насчет:

- Нет, слишком сложно.

Для начала начнем с чего-то попроще:

- М-да.

- Что, опять сложно? Тогда еще проще.

Обратитесь за заданием к агенту Intellij IDEA. Оно уже у него.

- Разрешите вопрос, сэр?

- Говори.

- А почему вы называете Intellij IDEA – агентом, сэр? Это же программа.

- Сам ты – программа.

IDEA – это искусственный интеллект и штатный сотрудник нашего корабля. Все понятно? Иди уже.

- Есть, идти выполнять приказание.