실행 가능한 문제

이미 Runnable 인터페이스와 이를 구현하는 Thread 클래스 에 익숙합니다 . 이 인터페이스가 어떻게 생겼는지 기억해 봅시다.


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 의 대안으로 사용할 수 있습니다 . 다음은 Callable 인터페이스 입니다 .


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

보시다시피 Runnable 과 마찬가지로 하나의 메서드만 있습니다. 이 메소드는 run 메소드 와 동일한 목적을 수행합니다 . 병렬 스레드에서 실행될 코드를 포함합니다. 차이점에 대해서는 반환 값을 살펴보십시오. 이제 인터페이스를 구현할 때 지정하는 모든 유형이 될 수 있습니다.


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

다른 예시:


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

여기에 또 다른 유용한 것이 있습니다. call 메서드는 Exception 을 던질 수 있습니다 . 즉, run 메서드와 달리 call 메서드 에서는 메서드 내에서 발생하는 확인된 예외를 처리할 필요가 없습니다.


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는 비동기(병렬) 계산의 결과( call 메서드 에서 반환된 값 )를 나타냅니다. 계산 완료 여부 확인, 계산 완료 대기, 계산 결과 확인 등을 할 수 있습니다.

미래 인터페이스의 방법

  • boolean isDone() — 이 작업(계산)이 완료되면 이 메서드는 true를 반환합니다. 정상적으로 종료되었거나 예외로 종료되었거나 취소된 작업은 완료된 것으로 간주됩니다.

  • V get() — 필요한 경우 이 메서드는 자신을 호출한 스레드를 차단하고 계산이 완료되면 계산 결과를 반환합니다.

  • V get(long timeout, TimeUnit unit) — 이전 메서드와 마찬가지로 이 메서드는 호출한 스레드를 차단하고 결과를 기다리지만 메서드 매개 변수에 지정된 시간 동안만 기다립니다.

  • boolean cancel(boolean mayInterruptIfRunning) — 이 메서드는 작업 실행을 중지하려고 시도합니다. 작업이 아직 실행되지 않은 경우 실행되지 않습니다. 작업이 진행 중인 경우 mayInterruptIfRunning 매개변수는 작업을 실행하는 스레드를 중단시키려는 시도가 있는지 여부를 결정합니다. cancel 메서드가 호출된 후 isDone 메서드는 항상 true 를 반환합니다 .

  • 부울 isCancelled() — 작업이 정상적으로 완료되기 전에 취소된 경우 이 메서드는 true를 반환합니다. cancel 메서드가 이전에 호출되어 true 를 반환한 경우 메서드는 항상 true 를 반환합니다 .

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