Masalah Runnable

Anda sudah biasa dengan antara muka Runnable dan kelas Thread yang melaksanakannya. Mari kita ingat semula rupa antara muka ini:


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

Ambil perhatian bahawa jenis pemulangan kaedah run adalah void . Tetapi bagaimana jika kita memerlukan benang kita untuk mengembalikan beberapa hasil kerjanya dalam bentuk nombor, rentetan atau objek lain? Kemudian kita mesti mencari penyelesaian. Sesuatu seperti ini:


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

Mari jalankan kaedah utama berikut :


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

Konsol akan memaparkan:

Nombor Fibonacci 10: 34

Kod ini mempunyai beberapa kelemahan. Sebagai contoh, hasil daripada panggilan ke kaedah gabungan , utas utama akan menyekat semasa kaedah printByIndex dilaksanakan.

Antara muka boleh dipanggil

Sekarang mari kita lihat antara muka yang Java sediakan kepada kita di luar kotak, yang boleh digunakan sebagai alternatif kepada Runnable . Ini ialah antara muka Boleh Panggil :


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

Seperti yang anda lihat, sama seperti Runnable , ia hanya mempunyai satu kaedah. Kaedah ini mempunyai tujuan yang sama seperti kaedah larian — ia mengandungi kod yang akan dilaksanakan dalam benang selari. Bagi perbezaan, lihat pada nilai pulangan. Kini ia boleh menjadi apa-apa jenis yang anda tentukan semasa melaksanakan antara muka:


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

Contoh yang lain:


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

Berikut adalah perkara lain yang berguna — kaedah panggilan boleh membuang Exception . Ini bermakna, tidak seperti kaedah larian , dalam kaedah panggilan kita tidak perlu mengendalikan pengecualian yang diperiksa yang berlaku di dalam kaedah:


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;

	}

}

Antara muka masa hadapan

Antara muka lain yang berfungsi rapat dengan Callable ialah Future . Masa hadapan mewakili hasil pengiraan tak segerak (selari) (nilai yang dikembalikan oleh kaedah panggilan ). Ia membolehkan anda menyemak sama ada pengiraan telah selesai, menunggu pengiraan selesai, mendapatkan hasil pengiraan dan banyak lagi.

Kaedah antara muka Masa Depan

  • boolean isDone() — kaedah ini kembali benar jika tugas ini (pengiraan) dilakukan. Tugasan yang berakhir seperti biasa, berakhir dengan pengecualian, atau telah dibatalkan dianggap selesai.

  • V get() — jika perlu, kaedah ini menyekat benang yang memanggilnya, dan mengembalikan hasil pengiraan apabila selesai.

  • V get(masa tamat lama, unit TimeUnit) — seperti kaedah sebelumnya, kaedah ini menyekat benang yang memanggilnya, menunggu keputusan, tetapi hanya untuk masa yang ditentukan oleh parameter kaedah.

  • boolean cancel(boolean mayInterruptIfRunning) — kaedah ini cuba menghentikan pelaksanaan tugas. Jika tugas itu belum mula berjalan, ia tidak akan berjalan. Jika tugas sedang dijalankan, maka parameter mayInterruptIfRunning menentukan sama ada percubaan akan dibuat untuk mengganggu utas yang melaksanakan tugas. Selepas kaedah batal dipanggil, kaedah isDone akan sentiasa mengembalikan true .

  • boolean isCancelled() — kaedah ini kembali benar jika tugasan dibatalkan sebelum ia selesai seperti biasa. Kaedah akan sentiasa kembali benar jika kaedah batal sebelum ini dipanggil dan dikembalikan benar .

Contoh kod menggunakan Callable dan 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;
 
	}
 
}

Mari jalankan kaedah utama berikut :


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

Konsol akan memaparkan:

Nombor Fibonacci 16: 610
Nombor Fibonacci 17: 987
Nombor Fibonacci 18: 1597
Nombor Fibonacci 19: 2584 Nombor
Fibonacci 10: 34 Nombor Fibonacci
11: 55 Nombor Fibonacci
12
: 89
Nombor Fibonacci 41: 89
Nombor Fibonacci 413 15: 377