การส่งงาน: Runnable กับ Callable
ส่งงาน Runnable และ Callable และรับผลลัพธ์ผ่าน Future
การส่งงาน: Runnable กับ Callable เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน
Runnable เทียบกับ Callable
อินเทอร์เฟซเชิงฟังก์ชันสองแบบใช้แทนงานที่ส่งไปยัง ExecutorService
- Runnable: ไม่มีค่าที่ส่งคืน และไม่สามารถประกาศข้อยกเว้นที่ตรวจสอบได้
- Callable<V>: ส่งคืนผลลัพธ์ชนิด V และสามารถส่งข้อยกเว้นที่ตรวจสอบได้
import java.util.concurrent.*;
// Runnable — no return value
Runnable r = () -> System.out.println("Running");
// Callable<Integer> — returns a value
Callable<Integer> c = () -> {
return 42; // or do computation
};submit(Runnable)
submit(Runnable) ส่งคืน Future<?> การเรียก get() กับค่านั้นจะส่งคืน null แต่ยืนยันได้ว่างานทำงานเสร็จแล้ว หรือส่งข้อยกเว้นหากงานล้มเหลว
ExecutorService pool = Executors.newFixedThreadPool(2);
Future<?> future = pool.submit(() ->
System.out.println("Task done on " + Thread.currentThread().getName())
);
future.get(); // waits for completion; returns null
System.out.println("Task confirmed complete");
pool.shutdown();submit(Callable<V>)
submit(Callable) ส่งคืน Future<V> เรียก future.get() เพื่อดึงผลลัพธ์ โดยการเรียกจะหยุดรอจนกว่างานจะเสร็จ
ExecutorService pool = Executors.newFixedThreadPool(2);
Callable<Integer> sumTask = () -> {
int sum = 0;
for (int i = 1; i <= 100; i++) sum += i;
return sum;
};
Future<Integer> future = pool.submit(sumTask);
Integer result = future.get(); // blocks until done
System.out.println("Sum 1..100 = " + result); // 5050
pool.shutdown();การส่ง Callable หลายรายการด้วย invokeAll
invokeAll() ส่งคอลเลกชันของ Callable และส่งคืนรายการของ Future โดย Future ทั้งหมดจะเสร็จสมบูรณ์ก่อนเมธอดจะส่งคืนค่า
List<Callable<String>> tasks = List.of(
() -> "Result A",
() -> "Result B",
() -> "Result C"
);
ExecutorService pool = Executors.newFixedThreadPool(3);
List<Future<String>> futures = pool.invokeAll(tasks);
for (Future<String> f : futures) {
System.out.println(f.get());
}
pool.shutdown();invokeAny: ผลลัพธ์แรกที่สำเร็จ
invokeAny() ส่งคอลเลกชันของ Callable และส่งคืนผลลัพธ์จากรายการแรกที่ทำงานสำเร็จ พร้อมยกเลิกรายการที่เหลือ
List<Callable<String>> candidates = List.of(
() -> { Thread.sleep(200); return "Slow"; },
() -> { Thread.sleep(50); return "Fast"; },
() -> { Thread.sleep(100); return "Medium"; }
);
ExecutorService pool = Executors.newFixedThreadPool(3);
String winner = pool.invokeAny(candidates);
System.out.println("First: " + winner); // Fast
pool.shutdown();execute() เทียบกับ submit()
execute(Runnable) เริ่มงานแล้วไม่รอผลลัพธ์ โดยไม่มี Future ส่งคืน และข้อยกเว้นจะสูญหายหากไม่เพิ่มตัวจัดการข้อยกเว้นที่ไม่ได้รับการจัดการ ส่วน submit() จะห่อหุ้มงานไว้ใน Future และเก็บข้อยกเว้นไว้ให้ดึงผ่าน get()
ExecutorService pool = Executors.newFixedThreadPool(2);
// execute: exception silently lost
pool.execute(() -> { throw new RuntimeException("Oops!"); });
// submit: exception captured in Future
Future<?> f = pool.submit(() -> { throw new RuntimeException("Oops!"); });
try {
f.get();
} catch (ExecutionException e) {
System.out.println("Caught: " + e.getCause().getMessage());
}
pool.shutdown();Callable กับข้อยกเว้นที่ตรวจสอบได้
ต่างจาก Runnable ตรงที่ Callable สามารถส่งข้อยกเว้นที่ตรวจสอบได้ จึงเหมาะกับการทำงานด้านการรับส่งข้อมูลและฐานข้อมูล
Callable<String> dbQuery = () -> {
// This compiles fine — checked exception is declared on Callable.call()
if (Math.random() < 0.5) throw new java.sql.SQLException("DB error");
return "row data";
};
Future<String> f = pool.submit(dbQuery);
try {
String data = f.get();
} catch (ExecutionException e) {
if (e.getCause() instanceof java.sql.SQLException) {
System.out.println("DB failed: " + e.getCause().getMessage());
}
}การทำการคำนวณที่ไม่ขึ้นต่อกันแบบขนาน
ใช้การส่ง Callable หลายรายการเพื่อทำการคำนวณที่ไม่ขึ้นต่อกันแบบขนาน
ExecutorService pool = Executors.newFixedThreadPool(4);
Future<Long> sumFuture = pool.submit(() -> LongStream.rangeClosed(1,1_000_000).sum());
Future<Long> prodFuture = pool.submit(() -> LongStream.rangeClosed(1,20).reduce(1L, (a,b)->a*b));
long sum = sumFuture.get();
long prod = prodFuture.get();
System.out.println("Sum: " + sum + ", 20!: " + prod);
pool.shutdown();การรวมผลลัพธ์จาก Callable
ส่งงานหลายรายการ แล้วรวมผลลัพธ์เมื่อทุกงานเสร็จสมบูรณ์
int N = 8;
ExecutorService pool = Executors.newFixedThreadPool(N);
List<Future<Integer>> futures = new ArrayList<>();
for (int i = 0; i < N; i++) {
final int chunk = i;
futures.add(pool.submit(() -> chunk * chunk)); // i^2
}
int total = 0;
for (Future<Integer> f : futures) total += f.get();
System.out.println("Sum of squares: " + total);
pool.shutdown();การหมดเวลาของ Callable
ส่งค่าการหมดเวลาให้กับ future.get(timeout, unit) เพื่อป้องกันการรอไม่สิ้นสุด
Future<String> f = pool.submit(() -> {
Thread.sleep(5000); // slow task
return "done";
});
try {
String result = f.get(1, TimeUnit.SECONDS);
} catch (TimeoutException e) {
System.out.println("Task timed out");
f.cancel(true); // interrupt the task
} catch (ExecutionException e) {
System.out.println("Task failed: " + e.getCause());
}แนวทางปฏิบัติที่ดี
สรุปแนวทางปฏิบัติที่ดี
- ใช้
submit()แทนexecute()เพราะข้อยกเว้นจะถูกเก็บไว้ - ใช้ Callable เมื่อต้องการค่าที่ส่งคืนหรือข้อยกเว้นที่ตรวจสอบได้
- เรียก
future.get()พร้อมการหมดเวลาเสมอ - ใช้ invokeAll สำหรับการประมวลผลเป็นชุด และใช้ invokeAny เมื่อผลลัพธ์แรกที่ตอบกลับเป็นผลลัพธ์ที่ต้องการ
- ปิดกลุ่มเธรดเสมอ
ตรวจสอบความเข้าใจอย่างรวดเร็ว
ข้อได้เปรียบสำคัญของ Callable เหนือ Runnable คืออะไร
สรุป: Runnable เทียบกับ Callable
ประเด็นสำคัญที่ควรจำ
- Runnable: ไม่มีค่า ไม่มีข้อยกเว้นที่ตรวจสอบได้ และ submit ส่งคืน Future<?> ที่มีค่า null
- Callable: ส่งคืน V และส่งข้อยกเว้นที่ตรวจสอบได้ได้ โดย submit ส่งคืน Future<V>
- invokeAll: ส่งงานเป็นชุดและรอให้ทั้งหมดเสร็จ
- invokeAny: ส่งคืนผลลัพธ์แรกที่สำเร็จและยกเลิกงานที่เหลือ
- ใช้ submit() เสมอเพื่อเก็บข้อยกเว้น
คำถามที่พบบ่อย
บทเรียน “การส่งงาน: Runnable กับ Callable” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การส่งงาน: Runnable กับ Callable” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การส่งงาน: Runnable กับ Callable”
ส่งงาน Runnable และ Callable และรับผลลัพธ์ผ่าน Future คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “การส่งงาน: Runnable กับ Callable” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม
ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- ExecutorService และชนิดพูลเธรด
- การส่งงาน: Runnable กับ Callable
- Future และการจัดการข้อผิดพลาด
- ScheduledExecutorService สำหรับงานที่ทำซ้ำ