Первая часть многопоточности были раньше. Эта глава считается дополнительной.
Заметки:
- Вычисление количества путей для потоков: (N*T)! / (N!)^T, где N — количество команд (сгенерированный байт-код), а T — количество потоков. Вообще впервые видела эту формулу, так что интересная информация.
- Используйте synchronized для уменьшения количества путей. Под путями понимается количество возможных вариантов, в каком порядке будет выполняться код. Например, если три потока А, Б и С, то возможные пути: АБС, АСБ, БАС, БСА, САБ, СБА.
- Атомарная операция — операция, выполнение которой не может быть прервано. Интересный факт: в Java a = 5 — атомарная операция, а b = 5L, не атомарная.
- Frame — для каждого вызова метода создается кадр с адресом возврата, значениями всех передаваемых параметров и локальных переменных из метода.
- Очень интересно переводить в байт-код и смотреть. Оказывается ++a — это 7 операций! И это, кстати, не атомарная операция.
- AtomicInteger всегда лучше synchronized.
- Серверная блокировка всегда лучше клиентской. Она сокращает дублирование, даёт более высокую производительность и снижает вероятность ошибки. И как бонус: единая политика использования, сокращает область видимости и всегда знаем где искать проблему.
- Старайтесь синхронизировать как можно меньше кода.
- Причины взаимной блокировки: взаимное исключение, блокировка с ожиданием, отсутствие вытеснения, циклическое ожидание.
- Взаимное исключение возникает когда несколько потоков должны использовать одни и те же ресурсы, а те ресурсы не могут использоваться несколькими потоками одновременно или существуют в ограниченном количестве. Типичный пример — подключение к БД.
- Как нарушить взаимное исключение: использовать ресурсы, поддерживающие многопоточку (AtomicInteger), увеличить количество ресурсов, чтобы оно достигло или превосходило количество конкурирующих потоков, проверять наличие всех свободных ресурсов перед попытками захвата.
- Блокировка с ожиданием — поток захватывает ресурс и не освобождает пока не захватит все остальные необходимые ресурсы и не завершит работу.
- Как нарушить блокировку с ожиданием — проверить перед захватом и освободить занятые ресурсы. Но тут есть проблемы: истощение (один поток и все ресурсы свободны), обратимая блокировка — все одновременно захватывают и освобождают ресурс.
- Отсутствие вытеснения — один поток не может отнимать ресурсы у другого потока. Если кто-то захватил ресурс, то второму потоку остается только ждать.
- Нарушение отсутствия вытеснения — если ресурс захвачен, то поток обращается к захватчику с просьбой освободить. Если тот ожидает другой ресурс, то освобождает. Из минусов: непросто реализовать.
- Циклическое ожидание — кот Мурзик ест из миски А. Кот Барсик ест из миски Б. Мурзик хочет попробовать корм из миски Б, а Барсик — из миски А. Оба кота ждут, когда освободятся миски.
- Нарушение циклического ожидания —заставляем потоки выделять ресурсы в постоянном порядке. При согласованном едином порядке захвата блокировки не будет. Но тут есть проблемы — порядок захвата может не соответствовать порядку использования. Например, вначале захватили и он потом не используется (Мурзик подошел к миске А и сидит возле неё, но не использует. Барсик ждёт). Еще минус, что ресурсы остаются заблокированными дольше необходимого. Также соблюдать фиксированный порядок возможно не всегда.
- Почти все стратегии приводят к ухудшению потоков или к интенсивному использованию процессора и ухудшению времени отклика. Бесплатный сыр бывает только в мышеловке.
- Тестирование методом Монте-Карло: это гибкие тесты, которые повторяем снова и снова со случайным изменением параметров. Также следует запускать тесты на машинах с разной нагрузкой.