1. Миссия
Разделите программу на несколько задач.
2. Современные веб-программы делят границы задач
Ограничен независимыми запросами клиентов. Просто запрос на задание.
3. Стратегия планирования задач
3.1 Серийное исполнение
Ужасная отзывчивость и пропускная способность.
3.2 Создайте тему для каждой задачи
В заключение:
- Задачи обрабатываются дочерними потоками, что повышает скорость отклика и пропускную способность.
- Код обработки задач должен быть потокобезопасным.
недостаточный:
- Накладные расходы времени жизни потока очень высоки.
- ЛФ. Если доступных для выполнения потоков больше, чем количество доступных процессоров, будут незанятые потоки, занимающие память, и возникнут другие потери производительности, когда большое количество потоков конкурирует за ЦП.
- стабильность. Различные платформы имеют ограничения на количество потоков, которые могут быть созданы.
4. Дизайн фреймворка Executor на Java
4.1 Концепция дизайна
Java предоставляет платформу Executor для выполнения задач. На основе модели производитель-потребитель. Отправка задачи означает, что операция эквивалентна производителю, а поток, выполняющий задачу, эквивалентен потребителю. (развязка, отсечение пиков)
public interface Executor {
void execute(Runnable command);
}
4.2 Стратегия исполнения
Код фиксации задачи разбросан по всему бизнес-коду программы.
Стратегия выполнения обрабатывается фреймворком единообразно.
Стратегия выполнения определяет аспекты выполнения задачи «Что, Где, Когда, Как», включая:
- В каком потоке выполняется задача?
- В каком порядке выполняются задачи (приоритет)?
- Сколько (сколько) задач может выполняться одновременно?
- Сколько задач стоит в очереди, ожидающих выполнения?
- Как система должна отклонить задачу?
- Какие действия необходимо выполнить до и после выполнения задачи?
Отделение отправки задачи от стратегии выполнения задачи облегчает выбор стратегии выполнения, которая наилучшим образом соответствует доступным аппаратным ресурсам на этапе развертывания.
4.3 Пул потоков
Платформа выполнения задач Executor будет «назначать поток каждой задаче», программируя стратегию на основе пула потоков.
Библиотека классов предоставляет гибкий пул потоков с некоторыми полезными значениями по умолчанию. Например, newFixedThreadpool.
- Веб-серверы больше не выходят из строя при высокой нагрузке.
- Но задачи всегда поступают быстрее, чем задачи выполняются, и серверу все равно может не хватать памяти.
4.4 Жизненный цикл Исполнителя
Executor расширяет интерфейс ExecutorService и добавляет несколько методов для управления жизненным циклом.
public interface ExecutorService extends Executor {
/**
* 平缓的关闭过程:不再接受新任务,等待已经提交的任务执行完成。
*/
void shutdown();
/**
* 粗暴的关闭过程:它将尝试取消所有运行中的任务,不在启动队列中尚未开始执行的任务。
*/
list<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit);
}
4.5 Отложенные задачи и периодические задачи
Таймер предоставляется в JAVA для управления задачами, рассчитанными по времени.
- Таймер выполняет временные задачи и создает только один поток.
- Таймер — это механизм планирования, основанный на абсолютном времени и чувствительный к системному времени.
- У таймера есть проблема с утечкой потока (таймер не перехватывает исключения, поток завершится, когда возникнет непроверенное исключение).
ScheduledThreadPoolExecutor лучше управляет запланированными задачами.
- Внутри него находится пул потоков.
- Это очень хорошо решает проблему утечки потока Timer.
Не подходит для распределенных сред.
5. Узнайте доступный параллелизм
В этой главе приведены несколько примеров для изучения параллелизма в запросе.
5.1 Пример: средство визуализации последовательных страниц
Предположим, страница = текстовая метка + изображение
Следующий код выполняет рендеринг последовательно.
public class SingleThreadRenderer {
void renderPage(CharSequence source) {
renderText(source);
List<ImageData> imageData = new ArrayList<ImageData>();
for (ImageInfo imageInfo : scanForImageInfo(source))
imageData.add(imageInfo.downloadImage());
for (ImageData data : imageData)
renderImage(data);
}
}
5.2 Task Callable и Future, несущие результаты
Выполняется как базовое представление задачи. Дефекты: 1. Нет возвращаемого значения. 2. Невозможно создать проверенное исключение.
- Вызываемый интерфейс
Это лучшая абстракция для задач, описывающая возвращаемые значения и исключения задачи.
public interface Callable<V> {
V call() throws Exception;
}
- Будущий интерфейс
Он представляет жизненный цикл задачи и предоставляет соответствующие методы для определения того, была ли задача завершена или отменена.
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws Exception;
V get(long timeout, TimeUnit unit);
}
5.3 Пример: реализация средства визуализации страницы с помощью Future
Разбейте процесс рендеринга на две задачи: одна — отрисовать весь текст, а вторая — загрузить все изображения.
代码略。
Рендеринг текста и рендеринг изображений выполняются одновременно.
5.4 Ограничения при распараллеливании разнородных задач
В приведенном выше примере скорость отрисовки текста намного выше скорости отрисовки картинок, итоговая эффективность выполнения программы мало чем отличается от последовательного выполнения, а код действительно усложняется.
Только когда большое количество взаимно независимых и изоморфных задач может обрабатываться одновременно, можно повысить производительность.
5.5CompletionService:Executor и BlockingQueue
Отправить набор задач, простой письменной форме.
@Test
public void test() throws Exception{
ExecutorService executor = Executors.newFixedThreadPool(5);
List<Future<String>> futures = new ArrayList();
for (int i=0; i<5; i++){
final int param = i;
Future<String> future = executor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(param * 1000);
return "result" + param;
}
});
futures.add(future);
}
for (int i=4; i>0; i--) {
System.out.println(futures.get(i).get());
}
}
CompletionService сочетает в себе функции Executor и BlockingQueue. Вы можете отправить ему вызываемую задачу для выполнения, а затем использовать методы взятия и опроса, такие как операции с очередью, для получения завершенного результата.
5.6 Пример: реализация средства визуализации страницы с CompletionService
Пример из книги: Чуть-чуть.
@Test
public void test() throws Exception{
ExecutorService executor = Executors.newFixedThreadPool(5);
CompletionService<String> completionService = new ExecutorCompletionService<>(executor);
for (int i=4; i>0; i--){
final int param = i;
completionService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(param * 1000);
return "result" + param;
}
});
}
for (int i=0; i<4; i++) {
System.out.println(completionService.take().get());
}
}
输出:
result1
result2
result3
result4
5.7 Установка временных ограничений для задач
Установите время для отдельных задач.
@Test
public void singleTaskTest(){
ExecutorService executor = Executors.newFixedThreadPool(5);
Future<String> future = executor.submit(new Callable<String>() {
@Override
public String call() {
try {
Thread.sleep(2000L);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("任务执行完毕...");
return "singleTask.";
}
});
try {
System.out.println(future.get(1, TimeUnit.SECONDS));
}catch (TimeoutException e){
System.out.println("任务超时...");
future.cancel(true); // 这句话的是否注销影响运行情况,原理未知?
}catch (InterruptedException e){
e.printStackTrace();
}catch (ExecutionException e){
e.printStackTrace();
}
}
5.8 Пример: портал бронирования стационарных телефонов
Не устанавливайте тайм-аут для нескольких задач.
6. Резюме
В этой главе в основном представлены преимущества и некоторые общие требования среды Java Executor.
Существует также гранулярность разделения задач, и границы задач должны быть проанализированы в соответствии с бизнес-сценарием.