0Pricing
Java Academy · บทเรียน

การส่งงาน: 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. ExecutorService และชนิดพูลเธรด
  2. การส่งงาน: Runnable กับ Callable
  3. Future และการจัดการข้อผิดพลาด
  4. ScheduledExecutorService สำหรับงานที่ทำซ้ำ
← กลับไปที่ Java Academy