В Java 5 возможности многопоточной разработки были значительно расширены по сравнению с предыдущими версиями. При написании многопоточного кода в Java 5 следует руководствоваться следующими правилами:
• Используйте потоково-безопасные коллекции.
• Используйте механизм Executor Framework для выполнения несвязанных задач.
• По возможности используйте неблокирующие решения.
• Некоторые библиотечные классы не являются потоково-безопасными.
Потоково-безопасные коллекции
Когда язык Java был еще молод, Даг Ли написал основополагающую книгу «Concurrent Programming in Java» [Lea99]. В ходе работы над книгой он разработал несколько потоково-безопасных коллекций, которые позднее были включены в JDK в пакете java.util.concurrent. Коллекции этого пакета безопасны в условиях многопоточного выполнения, к тому же они достаточно эффективно работают. Более того, реализация ConcurrentHashMap почти всегда работает лучше HashMap. К тому же она поддерживает возможность выполнения параллельных операций чтения и записи и содержит методы для выполнения стандартных составных операций, которые в общем случае не являются потоково-безопасными. Если ваша программа будет работать в среде Java 5, используйте ConcurrentHashMap в разработке.
Также в Java 5 были добавлены другие классы для поддержки расширенной многопоточности. Несколько примеров.
ReentrantLock | Блокировка, которая может устанавливаться и освобождаться в разных методах |
Semaphore | Реализация классического семафора (блокировка со счетчиком) |
CountDownLatch | Блокировка, которая ожидает заданного количества событий до освобождения всех ожидающих потоков. Позволяет организовать более или менее одновременный запуск нескольких потоков |
Рекомендация:
Знайте модели выполнения
В многопоточных приложениях возможно несколько моделей логического разбиения поведения программы. Но чтобы понять их, необходимо сначала познакомиться с некоторыми базовыми определениями.
Связанные ресурсы | Ресурсы с фиксированным размером или количеством, существующие в многопоточной среде, например подключения к базе данных или буферы чтения/записи |
Взаимное исключение | В любой момент времени с общими данными или с общим ресурсом может работать только один поток |
Зависание | Работа одного или нескольких потоков приостанавливается на слишком долгое время (или навсегда). Например, если высокоприоритетным потокам всегда предоставляется возможность отработать первыми, то низкоприоритетные потоки зависнут (при условии, что в системе постоянно появляются новые высокоприоритетные потоки) |
Взаимная блокировка (deadlock) | Два и более потока бесконечно ожидают завершения друг друга. Каждый поток захватил ресурс, необходимый для продолжения работы другого потока, и ни один поток не может завершиться без получения захваченного другим потоком ресурса |
Обратимая блокировка[57] (livelock) | Потоки не могут «разойтись» — каждый из потоков пытается выполнять свою работу, но обнаруживает, что другой поток стоит у него на пути. Потоки постоянно пытаются продолжить выполнение, но им это не удается в течение слишком долгого времени (или вообще не удается) |
Вооружившись этими определениями, можно переходить к обсуждению различных моделей выполнения, встречающихся в многопоточном программировании.