เหตุใดคุณจึงต้องการ 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()ซึ่งจะหยุดงานปัจจุบัน (งานที่กำลังดำเนินการ) และยกเลิกงานที่ค้างอยู่ทั้งหมด หลังจากนั้นโปรแกรมก็จบลงด้วยดี
แต่ทุกอย่างไม่ได้สมบูรณ์แบบเสมอไป เนื่องจากโปรแกรมของเราอาจมีสถานการณ์ที่งานอย่างใดอย่างหนึ่งที่หยิบขึ้นมาจากเธรดเดียวในกลุ่มของเราทำงานไม่ถูกต้องและแม้แต่ยุติเธรดของเรา เราสามารถจำลองสถานการณ์นี้เพื่อดูว่าexecutorServiceทำงานอย่างไรกับเธรดเดียวในกรณีนี้
ในการดำเนินการนี้ ในขณะที่งานหนึ่งกำลังดำเนินการอยู่ เราจะยุติเธรดของเราโดยใช้ เมธอดThread.currentThread().stop()ที่ไม่ปลอดภัยและล้าสมัย เรากำลังทำสิ่งนี้โดยเจตนาเพื่อจำลองสถานการณ์ที่งานอย่างใดอย่างหนึ่งยุติเธรด
เราจะเปลี่ยน วิธี การทำงานใน คลาส งาน :
@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
มาดูกันว่าผลลัพธ์จะเป็นอย่างไรเมื่อเธรดถูกขัดจังหวะเมื่อสิ้นสุดงาน #5:
คำขอที่ประมวลผล #1 บนเธรด id=16
คำขอที่ประมวลผล #2 บนเธรด id=16
คำขอที่ประมวลผล #3 บนเธรด id=16
คำขอที่ประมวลผล #4 บนเธรด id=16
คำขอที่ประมวลผล #6 บน เธรด id=17
คำขอที่ประมวลผล #7 บนเธรด id=17
…
คำขอที่ประมวลผล #29 บนเธรด id=17
เราเห็นว่าหลังจากเธรดถูกขัดจังหวะเมื่อสิ้นสุดภารกิจที่ 5 งานจะเริ่มดำเนินการในเธรดที่มีตัวระบุเป็น 17 แม้ว่าก่อนหน้านี้จะถูกดำเนินการบนเธรดที่มีตัวระบุเป็น 16 และเนื่องจากกลุ่มของเรามี เธรดเดียว สิ่งนี้สามารถหมายถึงสิ่งเดียวเท่านั้น: executorServiceแทนที่เธรดที่หยุดทำงานด้วยเธรดใหม่และดำเนินการงานต่อไป
ดังนั้น เราควรใช้newSingleThreadExecutorกับ single-thread pool เมื่อเราต้องการประมวลผลงานตามลำดับและทีละงานเท่านั้น และเราต้องการประมวลผลงานต่อจากคิวโดยไม่คำนึงว่างานก่อนหน้าจะเสร็จสิ้นหรือไม่ (เช่น กรณีที่งานหนึ่ง งานของเราฆ่าเธรด)
โรงงานด้าย
เมื่อพูดถึงการสร้างและการสร้างเธรดใหม่ เราอดไม่ได้ที่จะพูดถึงโรงงานด้าย.
กโรงงานด้ายเป็นวัตถุที่สร้างเธรดใหม่ตามต้องการ
เราสามารถสร้างโรงงานสร้างเธรดของเราเองและส่งอินสแตนซ์ของมันไปยังเมธอดExecutors.newSingleThreadExecutor (ThreadFactory threadFactory)
|
เราลบล้างวิธีการสร้างเธรดใหม่ โดยส่งชื่อเธรดไปยังคอนสตรัคเตอร์ |
|
เราเปลี่ยนชื่อและลำดับความสำคัญของเธรดที่สร้างขึ้น |
ดังนั้นเราจึงเห็นว่าเรามี เมธอดExecutors.newSingleThreadExecutorที่โอเวอร์โหลด 2 เมธอด อันหนึ่งไม่มีพารามิเตอร์ และอันที่สองมีพารามิเตอร์ThreadFactory
เมื่อใช้ThreadFactoryคุณสามารถกำหนดค่าเธรดที่สร้างขึ้นได้ตามต้องการ ตัวอย่างเช่น โดยการตั้งค่าลำดับความสำคัญ ใช้คลาสย่อยของเธรด การเพิ่ม UncaughtExceptionHandler ให้กับเธรด และอื่นๆ
GO TO FULL VERSION