การใช้งาน Thread ใน Java เวอร์ชั่นหลังๆ ไม่นิยมการสร้าง Thread ออกมาแบบตรงๆอีกต่อไป แต่จะนิยมใช้ ExecutorService เข้ามาช่วยในการสร้าง thread pool แทน เนื่องจากการเขียน Application แบบ Multi-threaded ที่ถูกต้องนั้น ไม่ใช่เรื่องง่าย แต่ ExecutorService จะทำให้เรื่องเหล่านี้ดูง่ายดายไปเลยทีเดียว

การใช้งาน ExecutorService นั้นก็เหมือนกับการสร้าง Thread ซึ่งเราก็ต้องสร้าง Class ที่ทำการ implement Runnable หรือ Callable มาเสียก่อน

โดยความแตกต่างระหว่าง Runnable กับ Callable ก็คือ Runnable ไม่สามารถคืน Exception หรือ Object ใดๆ กลับมายังผู้เรียกได้ แต่ Callable สามารถทำได้

Runnable

public class Task implements Runnable{

private int taskNo;
public Task(int taskNo){
this.taskNo = taskNo;
}
@Override
public void run() {
System.out.println(“Task”+taskNo);
}

}

Callable

public class Task implements Callable<String>{

private int taskNo;
public Task(int taskNo){
this.taskNo = taskNo;
}

@Override
public String call() throws Exception {
if(taskNo==9){
throw new NumberFormatException(“Task”+taskNo);
}
return “Task”+taskNo;
}

}

จะเห็นว่าใน Task ที่ implement Callable มีการ return String และ Throw Exception กลับไปยัง Caller ได้

โดยทำการทดสอบโดยการเขียนโค้ดเรียก Callable ดังนี้

ExecutorService pool = Executors.newSingleThreadExecutor();
Collection<Task> tasks = new LinkedList<Task>();
for (int i = 0; i < 10; i++) {
tasks.add(new Task(i));
}
try {
List< Future< String>> results = pool.invokeAll(tasks);
for (Future<String> future : results) {
System.out.println(future.get());
}
} catch (Exception ex) {
System.out.println(ex);
}finally{
pool.shutdown();
}

ผลลัพธ์

Task0
Task1
Task2
Task3
Task4
Task5
Task6
Task7
Task8
java.util.concurrent.ExecutionException: java.lang.NumberFormatException: Task9

โค้ดข้างต้นจะเป็นการสร้าง ExecutorService แบบ Thread เดียว ซึ่งแปลว่า Task ของเราก็จะถูกทำงานทีละ Task โดยเราจะต้องทำการสร้าง Collection สำหรับ Task ของเราทั้งหมดเสียก่อน แล้วจึงนำ collection นี้โยนไปให้ ExecutorService ทำงานผ่าน method invokeAll โดย method นี่จะส่งค่าจาก Task ของเรากลับมาอยู่ในรูปของ List ของ Class Future ซึ่งเราสามารถดึงค่าออกมาได้ด้วยการ get() และถ้าหากมีการโยน Exception ออกมา Exception เหล่านั้นก็จะถูก Catch ไว้ และส่วนสุดท้ายก็คือการ shutdown pool โดย thread ทั้งหมดที่ถูกสร้างก็จะถูก kill ไปตามระบบ

นอกจากที่ได้ยกตัวอย่างมาข้างต้นแล้ว ExecutorService ยังสามารถกำหนดระยะเวลาในการทำงานของ Pool ได้ด้วย หาก task ทำงานเกินระยะเวลาทีกำหนดก็จะถูก kill ไป