Защо може да се нуждаете от ExecutorService за 1 нишка?
Можете да използвате метода Executors.newSingleThreadExecutor , за да създадете ExecutorService с пул, който включва една нишка. Логиката на пула е следната:
- Услугата изпълнява само една задача наведнъж.
- Ако изпратим N задачи за изпълнение, всичките N задачи ще бъдат изпълнени една след друга от единичния поток.
- Ако нишката бъде прекъсната, ще бъде създадена нова нишка за изпълнение на всички останали задачи.
Нека си представим ситуация, в която нашата програма изисква следната функционалност:
Трябва да обработваме потребителски заявки в рамките на 30 секунди, но не повече от една заявка за единица време.
Създаваме клас задачи за обработка на потребителска заявка:
class Task implements Runnable {
private final int taskNumber;
public Task(int taskNumber) {
this.taskNumber = taskNumber;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
System.out.printf("Processed request #%d on thread id=%d\\n", taskNumber, Thread.currentThread().getId());
}
}
Класът моделира поведението при обработка на входяща заявка и показва нейния номер.
След това в основния метод създаваме ExecutorService за 1 нишка, която ще използваме за последователна обработка на входящи заявки. Тъй като условията на задачата предвиждат „в рамките на 30 секунди“, добавяме 30-секундно изчакване и след това принудително спираме ExecutorService .
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 1_000; i++) {
executorService.execute(new Task(i));
}
executorService.awaitTermination(30, TimeUnit.SECONDS);
executorService.shutdownNow();
}
След стартиране на програмата конзолата показва съобщения за обработка на заявка:
Обработена заявка #1 в нишка id=16
Обработена заявка #2 в нишка id=16
…
Обработена заявка #29 в нишка id=16
След като обработи заявките за 30 секунди, executorService извиква метода shutdownNow() , който спира текущата задача (тази, която се изпълнява) и отменя всички чакащи задачи. След това програмата приключва успешно.
Но не винаги всичко е толкова перфектно, защото нашата програма може лесно да има ситуация, в която една от задачите, взети от единствената нишка на нашия пул, работи неправилно и дори да прекрати нашата нишка. Можем да симулираме тази ситуация, за да разберем How executorService работи с една нишка в този случай.
За да направим това, докато една от задачите се изпълнява, ние прекратяваме нашата нишка, използвайки опасния и остарял метод Thread.currentThread().stop() . Правим това умишлено, за да симулираме ситуацията, при която една от задачите прекратява нишката.
Ще променим метода за изпълнение в класа Task :
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
if (taskNumber == 5) {
Thread.currentThread().stop();
}
System.out.printf("Processed request #%d on thread id=%d\\n", taskNumber, Thread.currentThread().getId());
}
Ще прекъснем задача №5.
Нека да видим How изглежда резултатът с нишката, прекъсната в края на задача #5:
Обработена заявка #1 в нишка id=16
Обработена заявка #2 в нишка id=16
Обработена заявка #3 в нишка id=16
Обработена заявка #4 в нишка id=16
Обработена заявка #6 в нишка id=17
Обработена заявка #7 на нишка id=17
…
Обработена заявка #29 на нишка id=17
Виждаме, че след като нишката е прекъсната в края на задача 5, задачите започват да се изпълняват в нишка, чийто идентификатор е 17, въпреки че преди това са бor изпълнени в нишката, чийто идентификатор е 16. И тъй като нашият пул има единична нишка, това може да означава само едно нещо: executorService замени спряната нишка с нова и продължи да изпълнява задачите.
По този начин трябва да използваме newSingleThreadExecutor с пул с една нишка, когато искаме да обработваме задачи последователно и само една по една, и искаме да продължим да обработваме задачи от опашката, независимо от завършването на предишната задача (напр. случаят, когато една от нашите задачи убива нишката).
ThreadFactory
Когато говорим за създаване и пресъздаване на теми, не можем да не споменемThreadFactory.
АThreadFactoryе обект, който създава нови нишки при поискване.
Можем да създадем наша собствена фабрика за създаване на нишки и да предадем нейно копие на метода Executors.newSingleThreadExecutor(ThreadFactory threadFactory) .
|
Ние отменяме метода за създаване на нова нишка, като предаваме име на нишка на конструктора. |
|
Променихме името и приоритета на създадената нишка. |
Така виждаме, че имаме 2 претоварени метода Executors.newSingleThreadExecutor . Един без параметри и втори с параметър ThreadFactory .
С помощта на ThreadFactory можете да конфигурирате създадените нишки според нуждите, например чрез задаване на приоритети, използване на подкласове на нишки, добавяне на UncaughtExceptionHandler към нишката и т.н.
GO TO FULL VERSION