เหตุใดคุณจึงต้องการส่วนต่อประสานผู้ดำเนินการ
ก่อน Java 5 คุณต้องเขียนการจัดการเธรดโค้ดของคุณเองในแอปพลิเคชันของคุณ นอกจากนี้ยังสร้างหัวข้อใหม่ออบเจกต์เป็นการดำเนินการที่ใช้ทรัพยากรมาก และไม่สมเหตุสมผลที่จะสร้างเธรดใหม่สำหรับทุกงานที่มีน้ำหนักเบา และเนื่องจากปัญหานี้เป็นที่คุ้นเคยกันดีสำหรับนักพัฒนาแอปพลิเคชันแบบมัลติเธรดทุกคน พวกเขาจึงตัดสินใจนำฟังก์ชันนี้มาไว้ใน Java เป็นเฟรมเวิร์กExecutor
ความคิดที่ยิ่งใหญ่คืออะไร? ง่ายมาก แทนที่จะสร้างเธรดใหม่สำหรับงานใหม่แต่ละงาน เธรดจะถูกเก็บไว้ในประเภท "ที่เก็บข้อมูล" และเมื่อมีงานใหม่เข้ามา เราจะดึงเธรดที่มีอยู่แทนที่จะสร้างเธรดใหม่
อินเทอร์เฟซหลักของเฟรมเวิร์กนี้คือExecutor , ExecutorServiceและScheduledExecutorServiceซึ่งแต่ละอินเทอร์เฟซจะขยายการทำงานของอินเทอร์เฟซก่อนหน้า

อินเทอร์เฟซ Executor เป็นอินเทอร์เฟซพื้นฐาน มันประกาศเป็นโมฆะ วิธี การดำเนินการ (คำสั่งที่รันได้) เดียว ที่นำไปใช้โดยวัตถุที่รันได้
อินเทอร์เฟ ซ ExecutorServiceน่าสนใจยิ่งขึ้น มีวิธีการจัดการการทำงานให้เสร็จสิ้นตลอดจนวิธีการส่งคืนผลลัพธ์บางประเภท มาดูวิธีการของมันให้ละเอียดยิ่งขึ้น:
วิธี | คำอธิบาย |
---|---|
เป็นโมฆะปิด (); | การเรียกวิธีนี้จะหยุดการทำงานของExecutorService งานทั้งหมดที่ส่งไปแล้วสำหรับการประมวลผลจะเสร็จสมบูรณ์ แต่งานใหม่จะไม่ได้รับการยอมรับ |
รายการ <Runnable> ปิดเดี๋ยวนี้ (); |
การเรียกวิธีนี้จะหยุดการทำงานของExecutorService Thread.interruptจะถูกเรียกสำหรับงานทั้งหมดที่ถูกส่งไปแล้วสำหรับการประมวลผล เมธอดนี้ส่งคืนรายการงานที่อยู่ในคิว เมธอดไม่รอให้งานทั้งหมดที่ "กำลังดำเนินการ" เสร็จสิ้นในเวลาที่เรียกใช้เมธอด คำเตือน: การเรียกวิธีนี้อาจทำให้ทรัพยากรรั่วไหล |
บูลีน isShutdown(); | ตรวจสอบว่าExecutorServiceหยุดทำงาน หรือไม่ |
บูลีน isTerminated(); | ส่งคืนค่าจริงหาก งานทั้งหมดเสร็จสิ้นหลังจากปิดระบบExecutorService จนกว่าจะมีการเรียกshutdown()หรือshutdownNow() ก็จะคืนค่า เป็นเท็จ เสมอ |
บูลีน waitTermination (การหมดเวลานาน, หน่วย TimeUnit) โยน InterruptedException; |
หลังจาก เรียกเมธอด shutdown()เมธอดนี้จะบล็อกเธรดที่ถูกเรียก จนกว่าจะมีเงื่อนไขข้อใดข้อหนึ่งต่อไปนี้เป็นจริง:
ส่งคืนค่าจริงหากงานทั้งหมดเสร็จสมบูรณ์ และค่าเท็จหากการหมดเวลาผ่านไปก่อนสิ้นสุด |
<T> อนาคต<T> ส่ง (งานที่เรียกได้<T>); |
เพิ่ม งาน Callableให้กับExecutorServiceและส่งคืนวัตถุที่ใช้อินเทอร์เฟซในอนาคต <T>คือประเภทของผลลัพธ์ของงานที่ผ่านไป |
<T> อนาคต<T> ส่ง (งานที่รันได้, ผลลัพธ์ T); |
เพิ่ม งาน ที่รันได้ให้กับExecutorServiceและส่งคืนวัตถุที่ใช้อินเทอร์เฟซในอนาคต พารามิเตอร์ผลลัพธ์ Tคือสิ่งที่ส่งคืนโดยการเรียกใช้เมธอดget()ในผลลัพธ์วัตถุในอนาคต |
อนาคต<?> ส่ง (งานที่รันได้); |
เพิ่ม งาน ที่รันได้ให้กับExecutorServiceและส่งคืนวัตถุที่ใช้อินเทอร์เฟซในอนาคต หากเราเรียก เมธอด get()บนผลลัพธ์ ของออบเจกต์ ในอนาคตเราจะได้ค่าว่าง |
<T> รายการ <Future<T>> เรียกใช้ทั้งหมด (Collection <? ขยายงาน Callable<T>>) พ่น InterruptedException; |
ส่งรายการงานที่เรียกได้ไปยังExecutorService ส่งคืนรายการฟิวเจอร์สซึ่งเราสามารถรับผลงานได้ รายการนี้จะถูกส่งกลับเมื่องานที่ส่งทั้งหมดเสร็จสิ้น หาก การรวบรวม งานถูกแก้ไขในขณะที่เมธอดกำลังรันอยู่ ผลลัพธ์ของเมธอดนี้จะไม่ถูกกำหนด |
<T> รายการ <Future<T>> เรียกใช้ทั้งหมด (Collection <? ขยายงาน Callable<T>>, หมดเวลานาน, หน่วย TimeUnit) โยน InterruptedException; |
ส่งรายการงานที่เรียกได้ไปยังExecutorService ส่งคืนรายการฟิวเจอร์สซึ่งเราสามารถรับผลงานได้ รายการนี้จะถูกส่งกลับเมื่องานที่ผ่านไปทั้งหมดเสร็จสิ้น หรือหลังจากหมดเวลาที่ส่งผ่านไปยังเมธอด แล้วแต่ว่าเหตุการณ์ใดจะเกิดขึ้นก่อน หากหมดเวลา งานที่ยังไม่เสร็จจะถูกยกเลิก หมายเหตุ: เป็นไปได้ว่างานที่ยกเลิกจะไม่หยุดทำงาน (เราจะเห็นผลข้างเคียงนี้ในตัวอย่าง) หาก การรวบรวม งานถูกแก้ไขในขณะที่เมธอดกำลังรันอยู่ ผลลัพธ์ของเมธอดนี้จะไม่ถูกกำหนด |
<T> T เรียกใช้ใดๆ (Collection <? ขยายงาน Callable<T>>) โยน InterruptedException, ExecutionException; |
ส่งรายการงานที่เรียกได้ไปยังExecutorService ส่งกลับผลลัพธ์ของงานอย่างใดอย่างหนึ่ง (ถ้ามี) ที่เสร็จสิ้นโดยไม่มีข้อยกเว้น (ถ้ามี) หาก การรวบรวม งานถูกแก้ไขในขณะที่เมธอดกำลังรันอยู่ ผลลัพธ์ของเมธอดนี้จะไม่ถูกกำหนด |
<T> T เรียกใช้ใดๆ (Collection <? ขยายงาน Callable<T>>, หมดเวลานาน, หน่วย TimeUnit) โยน InterruptedException, ExecutionException, TimeoutException; |
ส่งรายการงานที่เรียกได้ไปยังExecutorService ส่งกลับผลลัพธ์ของงานใดงานหนึ่ง (ถ้ามี) ที่เสร็จสิ้นโดยไม่ส่งข้อยกเว้นก่อนที่การหมดเวลาจะส่งผ่านไปยังเมธอดที่ผ่านไป หาก การรวบรวม งานถูกแก้ไขในขณะที่เมธอดกำลังรันอยู่ ผลลัพธ์ของเมธอดนี้จะไม่ถูกกำหนด |
มาดูตัวอย่างการทำงานกับExecutorServiceกัน
import java.util.List;
import java.util.concurrent.*;
public class ExecutorServiceTest {
public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
//Create an ExecutorService for 2 threads
java.util.concurrent.ExecutorService executorService = new ThreadPoolExecutor(2, 2, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));
// Create 5 tasks
MyRunnable task1 = new MyRunnable();
MyRunnable task2 = new MyRunnable();
MyRunnable task3 = new MyRunnable();
MyRunnable task4 = new MyRunnable();
MyRunnable task5 = new MyRunnable();
final List<MyRunnable> tasks = List.of(task1, task2, task3, task4, task5);
// Pass a list that contains the 5 tasks we created
final List<Future<Void>> futures = executorService.invokeAll(tasks, 6, TimeUnit.SECONDS);
System.out.println("Futures received");
// Stop the ExecutorService
executorService.shutdown();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(executorService.isShutdown());
System.out.println(executorService.isTerminated());
}
public static class MyRunnable implements Callable<Void> {
@Override
public void call() {
// Add 2 delays. When the ExecutorService is stopped, we will see which delay is in progress when the attempt is made to stop execution of the task
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
System.out.println("sleep 1: " + e.getMessage());
}
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
System.out.println("sleep 2: " + e.getMessage());
}
System.out.println("done");
return null;
}
}
}
เอาท์พุต:
สิ้น เสร็จ
สิ้น ฟิวเจอร์สได้รับ
สลีป 1: สลีปถูกขัดจังหวะ
สลีป 1: สลีปถูกขัดจังหวะ
เสร็จแล้ว
เสร็จแล้ว จริง
จริง
แต่ละงานทำงานเป็นเวลา 5 วินาที เราสร้างพูลสำหรับสองเธรด ดังนั้นเอาต์พุตสองบรรทัดแรกจึงเหมาะสมอย่างยิ่ง
หกวินาทีหลังจากเริ่มโปรแกรม เมธอด invokeAllจะหมดเวลาและผลลัพธ์จะถูกส่งกลับเป็นรายการFutures สามารถดูได้จากสตริงเอาต์พุตFutures ที่ได้รับ
หลังจากงานสองงานแรกเสร็จสิ้น งานอีกสองงานก็เริ่มต้นขึ้น แต่เนื่องจากการหมดเวลาที่ตั้งค่าไว้ใน เมธอด invokeAllผ่านไป งานทั้งสองนี้จึงไม่มีเวลาดำเนินการให้เสร็จ พวกเขาได้รับคำสั่ง"ยกเลิก" นั่นเป็นสาเหตุที่เอาต์พุตมีสองบรรทัดที่มีโหมดสลีป 1: โหมดสลีปถูกขัดจังหวะ
จากนั้นคุณจะเห็นอีกสองบรรทัดด้วยdone นี่คือผลข้างเคียงที่ฉันกล่าวถึงเมื่ออธิบายเมธอดinvokeAll
งานที่ห้าและสุดท้ายไม่เคยเริ่มต้นด้วยซ้ำ ดังนั้นเราจึงไม่เห็นอะไรเกี่ยวกับมันในผลลัพธ์
สองบรรทัดสุดท้ายเป็นผลมาจากการเรียกใช้เมธอดisShutdownและisTerminated
นอกจากนี้ยังน่าสนใจที่จะเรียกใช้ตัวอย่างนี้ในโหมดดีบักและดูสถานะงานหลังจากหมดเวลา (ตั้งค่าเบรกพอยต์ในบรรทัดด้วยexecutorService.shutdown(); ):

เราเห็นว่าสองงาน เสร็จ สมบูรณ์ตามปกติและสามงานถูก"ยกเลิก"
บริการผู้ดำเนินการตามกำหนดเวลา
เพื่อสรุปการสนทนาของเราเกี่ยวกับตัว ดำเนินการ ลองมาดูที่ScheduledExecutorService
มันมี 4 วิธี:
วิธี | คำอธิบาย |
---|---|
สาธารณะ ScheduledFuture<?> กำหนดการ (คำสั่งที่รันได้, หน่วงเวลานาน, หน่วย TimeUnit); | กำหนดเวลา งาน Runnable ที่ผ่านไป ให้ทำงานหนึ่งครั้งหลังจากการหน่วงเวลาที่ระบุเป็นอาร์กิวเมนต์ |
กำหนดการสาธารณะ <V> ScheduledFuture<V> (โทรได้ <V> โทรได้, หน่วงเวลานาน, หน่วย TimeUnit); | กำหนดเวลา งาน Callable ที่ผ่านไป ให้ทำงานหนึ่งครั้งหลังจากการหน่วงเวลาที่ระบุเป็นอาร์กิวเมนต์ |
สาธารณะ ScheduledFuture<?> scheduleAtFixedRate (คำสั่งที่รันได้, initialdelay ยาว, ระยะเวลานาน, หน่วย TimeUnit); | กำหนดการดำเนินการเป็นระยะของงานที่ผ่านไป ซึ่งจะดำเนินการเป็นครั้งแรกหลังจากinitialDelayและการรันแต่ละครั้งที่ตามมาจะเริ่มหลังจากช่วงเวลา |
สาธารณะ ScheduledFuture<?> scheduleWithFixedDelay (คำสั่งที่รันได้, initialdelay ที่ยาวนาน, ความล่าช้าที่ยาวนาน, หน่วย TimeUnit); | กำหนดการดำเนินการเป็นระยะของงานที่ผ่านไป ซึ่งจะดำเนินการเป็นครั้งแรกหลังจากinitialDelayและการรันแต่ละครั้งที่ตามมาจะเริ่มหลังจากการหน่วงเวลา (ระยะเวลาระหว่างการรันก่อนหน้าเสร็จสิ้นและการเริ่มต้นของรันปัจจุบัน) |
GO TO FULL VERSION