Vấn đề có thể chạy được

Bạn đã quen thuộc với giao diện Runnable và lớp Thread triển khai nó. Hãy nhớ lại giao diện này trông như thế nào:


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

Lưu ý rằng kiểu trả về của phương thức chạyvoid . Nhưng điều gì sẽ xảy ra nếu chúng ta cần luồng của mình trả về một số kết quả công việc của nó dưới dạng một số, một chuỗi hoặc một số đối tượng khác? Sau đó, chúng ta phải đưa ra một cách giải quyết. Một cái gì đó như thế này:


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);
 
	}
 
}

Hãy chạy phương thức chính sau :


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

Bảng điều khiển sẽ hiển thị:

Fibonacci số 10: 34

Mã này có một số nhược điểm. Ví dụ: do lệnh gọi phương thức nối , luồng chính sẽ bị chặn trong khi phương thức printByIndex được thực thi.

Giao diện có thể gọi

Bây giờ hãy xem giao diện mà Java cung cấp ngay cho chúng ta, giao diện này có thể được sử dụng thay thế cho Runnable . Đây là giao diện Callable :


public interface Callable<V> {
 
	V call() throws Exception;
 
}

Như bạn có thể thấy, giống như Runnable , nó chỉ có một phương thức. Phương thức này phục vụ cùng một mục đích như phương thức chạy — nó chứa mã sẽ được thực thi trong một luồng song song. Đối với sự khác biệt, hãy xem giá trị trả về. Bây giờ nó có thể là bất kỳ loại nào mà bạn chỉ định khi triển khai giao diện:


public class CurrentDate implements Callable<Long> {
 
	@Override
 
	public Long call() {
 
    		return new Date().getTime();
 
	}
 
}

Một vi dụ khac:


Callable<String> task = () -> {
 
	Thread.sleep(100);
 
	return "Done";
 
};

Đây là một thứ hữu ích khác — phương thức gọi có thể đưa ra một Ngoại lệ . Điều đó có nghĩa là, không giống như phương thức run , trong phương thức gọi , chúng ta không phải xử lý các ngoại lệ đã kiểm tra xảy ra bên trong phương thức:


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;

	}

}

giao diện tương lai

Một giao diện khác hoạt động chặt chẽ với CallableFuture . Tương lai đại diện cho kết quả của các phép tính không đồng bộ (song song) (giá trị được trả về bởi phương thức gọi ). Nó cho phép bạn kiểm tra xem các phép tính đã được thực hiện chưa, đợi các phép tính kết thúc, nhận kết quả của các phép tính, v.v.

Các phương thức của giao diện Tương lai

  • boolean isDone() — phương thức này trả về true nếu tác vụ này (tính toán) được hoàn thành. Các nhiệm vụ đã kết thúc bình thường, kết thúc với một ngoại lệ hoặc bị hủy bỏ được coi là đã hoàn thành.

  • V get() — nếu cần, phương thức này sẽ chặn chuỗi đã gọi nó và trả về kết quả tính toán khi chúng được thực hiện xong.

  • V get(long timeout, TimeUnit unit) — giống như phương thức trước, phương thức này chặn chuỗi đã gọi nó, chờ kết quả, nhưng chỉ trong khoảng thời gian được chỉ định bởi các tham số của phương thức.

  • boolean cancel(boolean mayInterruptIfRunning) — phương thức này cố gắng dừng việc thực thi tác vụ. Nếu tác vụ chưa bắt đầu chạy, nó sẽ không bao giờ chạy. Nếu tác vụ đang được tiến hành, thì tham số mayInterruptIfRunning sẽ xác định liệu một nỗ lực có được thực hiện để làm gián đoạn luồng đang thực thi tác vụ hay không. Sau khi phương thức hủy được gọi, phương thức isDone sẽ luôn trả về true .

  • boolean isCancelled() — phương thức này trả về true nếu tác vụ bị hủy trước khi nó kết thúc bình thường. Phương thức sẽ luôn trả về true nếu phương thức hủy trước đó được gọi và trả về true .

Ví dụ về mã sử dụng Callable và 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;
 
	}
 
}

Hãy chạy phương thức chính sau :


	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);
    		}
	}

Bảng điều khiển sẽ hiển thị:

Mã số Fibonacci 16: 610
Mã số Fibonacci 17: 987
Mã số Fibonacci 18: 1597 Mã
số Fibonacci 19: 2584 Mã
số Fibonacci 10: 34 Mã
số Fibonacci 11: 55 Mã số Fibonacci 12:
89 Mã số Fibonacci 13
: 144
Mã số Fibonacci 14: 233
Mã số Fibonacci 15: 377