Потоки являются фундаментальной частью платформы Java. Многоядерные процессоры — это обыденная реальность, а эффективное использование параллелизма стало необходимым для создания любого высокопроизводительного приложения. Улучшенная виртуальная машина Java, поддержка высокопроизводительных классов и богатый набор строительных блоков для задач распараллеливания стали в свое время прорывом в разработке параллельных приложений. В «Java Concurrency на практике» сами создатели прорывной технологии объясняют не только принципы работы, но и рассказывают о паттернах проектирования.
Легко создать конкурентную программу, которая вроде бы будет работать. Однако разработка, тестирование и отладка многопоточных программ доставляют много проблем. Код перестает работать именно тогда, как это важнее всего: при большой нагрузке. В «Java Concurrency на практике» вы найдете как теорию, так и конкретные методы создания надежных, масштабируемых и поддерживаемых параллельных приложений. Авторы не предлагают перечень API и механизмов параллелизма, они знакомят с правилами проектирования, паттернами и моделями, которые не зависят от версии Java и на протяжении многих лет остаются актуальными и эффективными.
Эта книга охватывает следующие темы:
• Базовые концепции параллелизма и безопасности потоков
• Методы построения и составления многопоточных классов
• Использование блоков параллелизма в java.util.concurrent
• Оптимизация производительности: что можно делать, а что не стоит и пытаться
• Тестирование параллельных программ
• Атомарные переменные, неблокирующие алгоритмы и модель памяти Java
Author(s): Брайан Гетц, Тим Пайерлс, Джошуа Блох, Джозеф Боубер, Дэвид Холмс, Даг Ли
Series: Для профессионалов
Edition: 1
Publisher: Питер
Year: 2020
Language: Russian
Commentary: True PDF
Pages: 464
City: СПб.
Отзывы
Листинги
Предисловие
Как пользоваться книгой
Примеры исходного кода
Благодарности
1.
Введение
1.1. Кратчайшая история конкурентности
1.2. Преимущества потоков
1.2.1. Задействование множества процессоров
1.2.2. Простота моделирования
1.2.3. Упрощенная обработка асинхронных событий
1.2.4. Более отзывчивые пользовательские интерфейсы
1.3. Риски для потоков
1.3.1. Угрозы безопасности
1.3.2. Сбои жизнеспособности
1.3.3. Угрозы производительности
1.4. Потоки есть везде
Часть I. Основы
2.
Потокобезопасность
2.1. Что такое потокобезопасность?
2.1.1. Пример: сервлет без поддержки внутреннего состояния
2.2. Атомарность
2.2.1. Состояния гонки
2.2.2. Пример: состояния гонки в ленивой инициализации
2.2.3. Составные действия
2.3. Блокировка
2.3.1. Внутренние замки
2.3.2. Повторная входимость
2.4. Защита состояния с помощью замков
2.5. Живучесть и производительность
3. Совместное использование объектов
3.1. Видимость
3.1.1. Устаревшие данные
3.1.2. Неатомарные 64-разрядные операции
3.1.3. Блокировка и видимость
3.1.4. Волатильные переменные
3.2. Публикация и ускользание
3.2.1. Приемы безопасного конструирования
3.3. Ограничение одним потоком
3.3.1. Узкоспециальное ограничение одним потоком
3.3.2. Ограничение стеком
3.3.3. ThreadLocal
3.4. Немутируемость
3.4.1. Финальные поля
3.4.2. Пример: использование volatile для публикации немутируемых объектов
3.5. Безопасная публикация
3.5.1. Ненадлежащая публикация: хорошие объекты становятся плохими
3.5.2. Немутируемые объекты и безопасность при инициализации
3.5.3. Приемы безопасной публикации
3.5.4. Фактически немутируемые объекты
3.5.5. Мутируемые объекты
3.5.6. Безопасное совместное использование объектов
4. Составление объектов
4.1. Проектирование потокобезопасного класса
4.1.1. Сбор требований к синхронизации
4.1.2. Операции, зависимые от состояния
4.1.3. Владение состоянием
4.2. Ограничение одним экземпляром
4.2.1. Мониторный шаблон Java
4.2.2. Пример: отслеживание такси
4.3. Делегирование потокобезопасности
4.3.1. Пример: отслеживатель такси с использованием делегирования
4.3.2. Независимые переменные состояния
4.3.3. Случаи безуспешного делегирования
4.3.4. Публикация базовых переменных состояния
4.3.5. Пример: отслеживатель такси, публикующий свое состояние
4.4. Добавление функциональности в существующие потокобезопасные классы
4.4.1. Блокировка на стороне клиента
4.4.2. Составление
4.5. Документирование политик синхронизации
4.5.1. Толкование расплывчатой документации
5.
Строительные блоки
5.1. Синхронизированные коллекции
5.1.1. Проблемы синхронизированных коллекций
5.1.2. Итераторы и исключение ConcurrentModificationException
5.1.3. Скрытые итераторы
5.2. Конкурентные коллекции
5.2.1. ConcurrentHashMap
5.2.2. Дополнительные атомарные операции над ассоциативным массивом
5.2.3. CopyOnWriteArrayList
5.3. Блокирующие очереди и шаблон производитель-потребитель
5.3.1. Пример: поиск на рабочем столе
5.3.2. Серийное ограничение одним потоком
5.3.3. Двухсторонние очереди и кража работы
5.4. Блокирующие и прерываемые методы
5.5. Синхронизаторы
5.5.1. Защелки
5.5.2. FutureTask
5.5.3. Семафоры
5.5.4. Барьеры
5.6. Создание эффективного масштабируемого кэша результатов
Итоги
Часть II. Структурирование конкурентных приложений
6.
Выполнение задач
6.1. Выполнение задач в потоках
6.1.1. Последовательное выполнение задач
6.1.2. Явное создание потоков для задач
6.1.3. Недостатки создания неограниченных потоков
6.2. Структура Executor
6.2.1. Пример: веб-сервер с использованием Executor
6.2.2. Политики выполнения
6.2.3. Пулы потоков
6.2.4. Жизненный цикл исполнителя Executor
6.2.5. Отложенные и периодические задачи
6.3. Поиск эксплуатационно-пригодного параллелизма
6.3.1. Пример: последовательный страничный отрисовщик
6.3.2. Задачи, приносящие результаты: Callable и Future
6.3.3. Пример: страничный отрисовщик с объектом Future
6.3.4. Ограничения параллелизации разнородных задач
6.3.5. CompletionService: исполнитель Executor встречается с очередью BlockingQueue
6.3.6. Пример: страничный отрисовщик со службой CompletionService
6.3.7. Наложение временных ограничений на задачи
6.3.8. Пример: портал бронирования поездок
Итоги
7. Отмена и выключение
7.1. Отмена задачи
7.1.1. Прерывание
7.1.2. Политики прерывания
7.1.3. Отклик на прерывание
7.1.4. Пример: хронометрированный прогон
7.1.5. Отмена с помощью Future
7.1.6. Работа с непрерываемым блокированием
7.1.7. Инкапсуляция нестандартной отмены с помощью newTaskFor
7.2. Остановка поточной службы
7.2.1. Пример: служба журналирования
7.2.2. Выключение службы ExecutorService
7.2.3. Ядовитые таблетки
7.2.4. Пример: служба однократного выполнения
7.3. Обработка аномальной терминации потоков
7.3.1. Обработчики неотловленных исключений
7.4. Выключение JVM
7.4.1. Хуки
7.4.2. Потоки-демоны
7.4.3. Финализаторы
Итоги
8. Применение пулов потоков
8.1. Неявные стыковки между задачами и политиками выполнения
8.1.1. Взаимная блокировка с ресурсным голоданием
8.1.2. Длительные задачи
8.2. Определение размера пула потоков
8.3. Конфигурирование класса ThreadPoolExecutor
8.3.1. Создание и демонтаж потоков
8.3.2. Управление задачами очереди
8.3.4. Фабрики потоков
8.3.5. Настройка класса ThreadPoolExecutor после конструирования
8.4. Расширение класса ThreadPoolExecutor
8.4.1. Пример: добавление статистики в пул потоков
8.5. Параллелизация рекурсивных алгоритмов
8.5.1. Пример: структура головоломки
Итоги
9. Приложения с GUI
9.1. Почему GUI‑интерфейсы являются однопоточными?
9.1.1. Последовательная обработка событий
9.1.2. Ограничение одним потоком в Swing
9.2. Кратковременные задачи GUI
9.3. Длительные задачи GUI
9.3.1. Отмена
9.3.2. Индикация хода выполнения и завершения
9.3.3. SwingWorker
9.4. Совместные модели данных
9.4.1. Потокобезопасные модели данных
9.4.2. Раздвоенные модели данных
9.5. Другие формы однопоточных подсистем
Итоги
Часть III. Жизнеспособность, производительность и тестирование
10. Предотвращение сбоев жизнеспособности
10.1. Взаимная блокировка
10.1.1. Взаимные блокировки из‑за замковой упорядоченности
10.1.3. Взаимные блокировки между взаимодействующими объектами
10.1.4. Открытые вызовы
10.1.5. Ресурсные взаимные блокировки
10.2. Предотвращение и диагностирование взаимной блокировки
10.2.1. Хронометрированные замки
10.2.2. Анализ взаимной блокировки с помощью поточных дампов
10.3. Другие сбои жизнеспособности
10.3.1. Голодание
10.3.2. Слабая отзывчивость
10.3.3. Активная блокировка
Итоги
11. Производительность и масштабирование
11.1. Некоторые мысли о производительности
11.1.1. Производительность и масштабируемость
11.1.2. Оценивание компромиссов производительности
11.2. Закон Амдала
11.2.1. Пример: сериализация, скрытая в программных структурах
11.2.2. Качественное применение закона Амдала
11.3. Стоимость, вносимая потоком
11.3.1. Переключение контекста
11.3.2. Синхронизации памяти
11.3.3. Блокирование
11.4. Сокращение конфликта блокировки
11.4.1. Сужение области действия замка («вошел, вышел»)
11.4.2. Сокращение степени детализации замка
11.4.3. Замковое расщепление на полосы
11.4.4. Недопущение горячих полей
11.4.5. Альтернативы исключающим блокировкам
11.4.6. Мониторинг задействованности процессоров
11.4.7. Объединению объектов в пул — «нет»!
11.5. Пример: сравнение производительности ассоциативного массива Map
11.6. Сокращение издержек на переключение контекста
Итоги
12. Тестирование конкурентных программ
12.1. Тестирование на правильность
12.1.1. Элементарные модульные тесты
12.1.2. Тестирование блокирующих операций
12.1.3. Тестирование на безопасность
12.1.4. Тестирование на управление ресурсами
12.1.5. Использование обратных вызовов
12.1.6. Генерирование большего числа перемежений
12.2. Тестирование на производительность
12.2.1. Расширение теста PutTakeTest за счет хронометрирования
12.2.2. Сравнение многочисленных алгоритмов
12.2.3. Измерение отзывчивости
12.3. Предотвращение ловушек при проведении тестов на производительность
12.3.1. Сбор мусора
12.3.2. Динамическая компиляция
12.3.3. Нереалистичный отбор ветвей кода
12.3.4. Нереалистичные уровни конфликта
12.3.5. Устранение мертвого кода
12.4. Комплементарные подходы к тестированию
12.4.1. Ревизия кода
12.4.2. Инструменты статического анализа
12.4.3. Аспектно-ориентированные методы тестирования
12.4.4. Средства профилирования и мониторинга
Итоги
Часть IV. Продвинутые темы
13. Явные замки
13.1. Lock и ReentrantLock
13.1.1. Опрашиваемое и хронометрируемое приобретение замка
13.1.2. Прерываемое приобретение замка
13.1.3. Неблочно структурированная замковая защита
13.2. Соображения по поводу производительности
13.3. Справедливость
13.4. Выбор между synchronized и ReentrantLock
13.5. Замки чтения-записи
Итоги
14. Построение настраиваемых синхронизаторов
14.1. Управление зависимостью от состояния
14.1.1. Пример: распространение сбоя предусловия на вызывающие элементы кода
14.1.2. Пример: грубое блокирование с помощью опрашивания и сна
14.1.3. Очереди условий во спасение
14.2. Использование очередей условий
14.2.1. Условный предикат
14.2.2. Слишком ранее пробуждение
14.2.3. Пропущенные сигналы
14.2.4. Уведомление
14.2.5. Пример: шлюзовый класс
14.2.6. Вопросы безопасности подклассов
14.2.7. Инкапсулирование очередей условий
14.2.8. Протоколы входа и выхода
14.3. Явные объекты условий
14.4. Анатомия синхронизатора
14.5. AbstractQueuedSynchronizer
14.5.1. Простая защелка
14.6. AQS в классах синхронизатора библиотеки java.util.concurrent
14.6.1. ReentrantLock
14.6.2. Semaphore и CountDownLatch
14.6.3. FutureTask
14.6.4. ReentrantReadWriteLock
Итоги
15. Атомарные переменные и неблокирующая синхронизация
15.1. Недостатки замковой защиты
15.2.1. Сравнить и обменять
15.2.3. Поддержка операции CAS в JVM
15.3. Классы атомарных переменных
15.3.1. Атомарные компоненты в качестве «более качественных волатильных»
15.3.2. Сравнение производительности: замки против атомарных переменных
15.4. Неблокирующие алгоритмы
15.4.1. Неблокирующий стек
15.4.2. Неблокирующий связный список
15.4.3. Обновители атомарных полей
15.4.4. Проблема ABA
Итоги
16. Модель памяти Java
16.1. Что такое модель памяти, и зачем она нужна?
16.1.1. Платформенные модели памяти
16.1.2. Переупорядочивание
16.1.3. Модель памяти Java в менее чем 500 словах
16.1.4. Выезд за счет синхронизации
16.2. Публикация
16.2.1. Небезопасная публикация
16.2.2. Безопасная публикация
16.2.4. Блокировка с двойной проверкой
16.3. Безопасность инициализации
Итоги
Приложение А. Аннотации для конкурентности
A.1. Аннотации классов
A.2. Аннотации полей и методов