Java ExecutorCompletionService: รับผลตามงานที่เสร็จก่อน
ถ้าส่ง Future สามงานแล้วเรียก get() ตามลำดับส่ง งานแรกที่ช้าจะบังผลของงานสองและสามทั้งที่เสร็จแล้วครับ ExecutorCompletionService วาง Future ที่เสร็จลง completion queue ทำให้ consumer รับตามลำดับเสร็จ เหมือนเคาน์เตอร์เรียกลูกค้าตามอาหารที่ครัวทำเสร็จ ไม่ใช่ตามเลขใบสั่ง
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(3);
try {
ExecutorCompletionService<String> completed = new ExecutorCompletionService<>(pool);
completed.submit(() -> { Thread.sleep(80); return "slow"; });
completed.submit(() -> { Thread.sleep(10); return "fast"; });
completed.submit(() -> { Thread.sleep(40); return "middle"; });
List<String> order = new ArrayList<>();
for (int i = 0; i < 3; i++) order.add(completed.take().get());
assert order.equals(List.of("fast", "middle", "slow")) : order;
} finally {
pool.shutdownNow();
}
}
}
Trade-off
เวลาและจำนวน thread ยังขึ้นกับ Executor เดิม CompletionService ไม่เพิ่มการยกเลิก, timeout หรือ backpressure ให้อัตโนมัติ ต้องเก็บ Future ที่ submit ถ้าต้อง cancel และใช้ poll(timeout) เมื่อ consumer ห้ามรอไม่สิ้นสุด ถ้าผลลัพธ์ต้องกลับตาม input order ให้ใส่ index ใน result แล้วจัดเรียงภายหลัง
Recap
- ใช้เมื่ออยากประมวลผลผลลัพธ์ที่พร้อมก่อน
- กำหนด timeout และ cancellation policy เอง
- ปิด Executor ใน
finally - แนบ index ถ้าปลายทางต้องคืนลำดับเดิม