ปัญหาที่รันได้

คุณคุ้นเคยกับ อินเทอร์เฟซ ที่รันได้และคลาสเธรดที่ใช้งานแล้ว จำหน้าตาของอินเทอร์เฟซนี้:

public interface Runnable {
	public abstract void run();
}

โปรดทราบว่า ประเภทการ ส่งคืนของเมธอดrun จะเป็น void แต่ถ้าเราต้องการให้เธรดของเราส่งคืนผลงานบางอย่างในรูปแบบของตัวเลข สตริง หรือวัตถุอื่นๆ จากนั้นเราจะต้องคิดวิธีแก้ปัญหา สิ่งนี้:

public class Fibonacci implements Runnable {



	private final int index;

	private int result;



	public Fibonacci(int index) {

    		this.index = index;

	}



	@Override

	public void run() {

    		int first = 0;

    		int second = 1;

    		if (index == 1) {

        			result = first;

    		} else if (index == 2) {

        			result = second;

    		} else {

        			for (int i = 0; i < index - 2; i++) {

            				int temp = second;

            				second += first;

            				first = temp;

        			}



            			result = second;

    		}

	}



	public static void printByIndex(int index) throws InterruptedException {

    		Fibonacci fibonacci = new Fibonacci(index);

    		Thread thread = new Thread(fibonacci);

    		thread.start();

    		thread.join();

    		System.out.println("Fibonacci number " + index + ": " + fibonacci.result);

	}

}

ลองใช้ วิธี หลัก ต่อไปนี้ :

public static void main(String[] args) throws Exception {
    		Fibonacci.printByIndex(10);
}

คอนโซลจะแสดง:

หมายเลขฟีโบนักชี 10: 34

รหัสนี้มีข้อบกพร่องหลายประการ ตัวอย่างเช่น จากการเรียกเมธอดjoinเธรดหลักจะบล็อกในขณะที่ เมธอด printByIndexถูกดำเนินการ

อินเทอร์เฟซที่โทรได้

ทีนี้มาดูอินเทอร์เฟซที่ Java มอบให้เราทันที ซึ่งสามารถใช้เป็นทางเลือกแทนRunnableได้ นี่คือ อินเทอร์เฟซ ที่โทรได้ :

public interface Callable<V> {

	V call() throws Exception;

}

อย่างที่คุณเห็น เช่นเดียวกับRunnableมีเพียงวิธีเดียวเท่านั้น วิธีการนี้มีจุดประสงค์เดียวกันกับ วิธี การรัน — มันมีโค้ดที่จะถูกดำเนินการในเธรดคู่ขนาน สำหรับความแตกต่างให้ดูที่ค่าส่งคืน ตอนนี้สามารถเป็นประเภทใดก็ได้ที่คุณระบุเมื่อใช้งานอินเทอร์เฟซ:

public class CurrentDate implements Callable<Long> {

	@Override

	public Long call() {

    		return new Date().getTime();

	}

}

ตัวอย่างอื่น:

Callable<String> task = () -> {

	Thread.sleep(100);

	return "Done";

};

มีอย่างอื่นที่เป็นประโยชน์ — วิธี การโทรสามารถโยนข้อยกเว้น ซึ่งหมายความว่า ไม่เหมือนกับ เมธอด runในเมธอดการโทรเราไม่ต้องจัดการข้อยกเว้นที่เลือกซึ่งเกิดขึ้นภายในเมธอด:

public class Sleep implements Runnable {

	@Override

	public void run() {

    	    try {

        	        Thread.sleep(1000);

    	    } catch (InterruptedException ignored) {

    	    }

	}

}
public class Sleep implements Callable {

	@Override

	public Object call() throws InterruptedException {

    	    Thread.sleep(1000);

    	    return null;

	}

}

อินเทอร์เฟซในอนาคต

อินเทอร์เฟซอื่นที่ทำงานอย่างใกล้ชิดกับCallableคือFuture Futureแสดงผลของการคำนวณแบบอะซิงโครนัส (แบบขนาน) (ค่าที่ส่งคืนโดยเมธอดการโทร ) ซึ่งช่วยให้คุณตรวจสอบว่าการคำนวณเสร็จสิ้นหรือไม่ รอให้การคำนวณเสร็จสิ้น รับผลการคำนวณ และอื่นๆ

วิธีการของส่วนต่อประสานในอนาคต

  • บูลีน isDone() — วิธีนี้จะคืนค่าจริงหากงานนี้ (การคำนวณ) เสร็จสิ้น งานที่สิ้นสุดตามปกติ สิ้นสุดด้วยข้อยกเว้น หรือถูกยกเลิกถือว่าเสร็จสิ้น

  • V get() — หากจำเป็น เมธอดนี้จะบล็อกเธรดที่เรียกใช้ และส่งคืนผลลัพธ์ของการคำนวณเมื่อเสร็จสิ้น

  • V get(long timeout, TimeUnit unit) — เช่นเดียวกับวิธีก่อนหน้า เมธอดนี้จะบล็อกเธรดที่เรียกมัน รอผลลัพธ์ แต่เฉพาะเวลาที่ระบุโดยพารามิเตอร์เมธอดเท่านั้น

  • การยกเลิกบูลีน (บูลีน mayInterruptIfRunning) - วิธีนี้พยายามหยุดการทำงานของงาน ถ้างานยังไม่เริ่มทำงาน ก็จะไม่ทำงาน หากงานอยู่ระหว่างดำเนินการ พารามิเตอร์ mayInterruptIfRunningจะพิจารณาว่าจะมีการพยายามขัดจังหวะเธรดที่เรียกใช้งานหรือไม่ หลังจากเรียกเมธอดcancel เมธอด isDoneจะคืนค่า trueเสมอ

  • บูลีน isCancelled() — วิธีนี้จะคืนค่าจริงหากงานถูกยกเลิกก่อนที่จะเสร็จสิ้นตามปกติ เมธอดจะคืนค่าจริง เสมอ หาก เมธอด ยกเลิกถูกเรียกก่อนหน้านี้และส่งคืนค่าจริง

ตัวอย่างโค้ดที่ใช้ Callable และ Future

import java.util.HashMap;

import java.util.Map;

import java.util.concurrent.*;



public class Fibonacci implements Callable<Integer> {



	private final int index;



	public Fibonacci(int index) {

    		this.index = index;

	}



	@Override

	public Integer call() {

    		int first = 0;

    		int second = 1;

    		if (index == 1) {

        			return first;

    		} else if (index == 2) {

        			return second;

    		} else {

        		for (int i = 0; i < index - 2; i++) {

            			int temp = second;

            			second += first;

            			first = temp;

        		}



        			return second;

    		}

	}



	public static Future<Integer> calculateAsync(int index) throws Exception {

    		Fibonacci fibonacci = new Fibonacci(index);



    		// The future object will represent the result of running the fibonacci task.

    		FutureTask<Integer> future = new FutureTask<>(fibonacci);



    		// Because the FutureTask class implements both the Future interface and the Runnable interface,

	 	// you can pass instances of it to the Thread constructor

    		Thread thread = new Thread(future);

    		thread.start();



    		return future;

	}

}

ลองใช้ วิธี หลัก ต่อไปนี้ :

public static void main(String[] args) throws Exception {
    		Map<Integer, Future<Integer>> tasks = new HashMap<>();
    		for (int i = 10; i < 20; i++) {
        			tasks.put(i, Fibonacci.calculateAsync(i));
    		}

    		for (Map.Entry<Integer, Future<Integer>> entry : tasks.entrySet()) {
        			Future<Integer> task = entry.getValue();
        			int index = entry.getKey();
        			int result;
        			// Check whether the task is done
        			if (task.isDone()) {
            				// Get the result of the calculations
            				result = task.get();
        			} else {
            				try {
                				// Wait another 100 milliseconds for the result of the calculations
                				result = task.get(100, TimeUnit.MILLISECONDS);
            				} catch (TimeoutException e) {
                				// Interrupt the task
                				task.cancel(true);
                				System.out.println("Fibonacci number " + index + " could not be calculated in the allotted time.");
                				return;
            				}
        			}
        			System.out.println("Fibonacci number " + index + ": " + result);
    		}
}

คอนโซลจะแสดง:

หมายเลขฟีโบนักชี 16: 610
หมายเลขฟีโบนักชี 17: 987
หมายเลขฟีโบนักชี 18: 1597 หมายเลข
ฟีโบนักชี 19: 2584 หมายเลข
ฟีโบนักชี 10: 34 หมายเลข ฟีโบนักชี 11: 55 หมายเลขฟีโบ นักชี 12: 89 หมายเลข
ฟีโบนักชี 13: 144 หมายเลขฟี โบนักชี 14: 233 หมายเลขฟีโบนักชี 15:377