[JAVA] ExecutorService 인터페이스

ExecutorService 인터페이스는 Executor의 확장된 버전입니다.  스레드의 생명 주기에 대한 메서드가 있어서 스레드 관리를 훨씬 더 편리하게 제공합니다. 

 

ExecutorService 메서드

  • shutdown : 이전에 제출된 작업은 순차적으로 실행되면서 종료됩니다. 새로운 작업은 허용하지 않습니다. 이전에 제출된 작업이 실행은 되지만 완료할 때까지는 기다리지 않습니다.
  • shutdownNow : 이전에 제출된 작업이 실행되지 않고 바로 종료되면서 가지고 있던 작업 목록을 반환합니다. shoutdown과 마찬가지로 실행 중인 작업이 종료될 때까지 기다리지 않습니다.
  • isShutdown : ExecutorService가 shutdown 상태이면 true 아니면 false를 반환합니다.
  • isTerminated : shutdown 상태에서 모든 작업이 완료되면 true 아니면 false를 반환합니다.
  • awaitTermination : ExecutorService가 종료될때까지 대기하거나 특정 시간동안 대기하는 기능을 제공합니다.
  • submit(Callable) : 작업의 결과를 알고 싶을때 execute 메서드 대신에 사용합니다. Future 인스턴스를 반환하는데 get 메서드를 사용하여 작업의 결과를 알 수 있습니다.
  • submit(Runnable, T) : 작업의 결과를 알 수 있는 Future를 반환합니다. get 메서드를 사용하여 2번째 파라미터로 넣은 결과값을 반환합니다.
  • submit(Runnalbe) : 작업의 결과를 알 수 있는 Future를 반환합니다. get 메서드를 사용하여 결과가 성공적이면 null을 반환합니다.
  • invokeAll(Collection) : 모든 작업이 완료될 때 그 상태와 결과를 담고 있는 Future들의 목록을 반환합니다. Future 인스턴스에 isDone 메서드는 작업이 완료되었는지 여부입니다.
  • invokeAll(Collection, long, TimeUnit) : 모든 작업이 완료되거나 제한 시간이 만료되면 그 상태와 결과를 담고 있는 Future들의 목록을 반환합니다. 반환 시 완료되지 않은 작업은 취소됩니다.
  • invokeAny(Collection) : 주어진 작업들을 실행하고 성공적으로 완료된 작업이 있으면 나머지 작업들은 취소됩니다.
  • invokeAny(Collection, long, TimeUnit) : 주어진 시간이 경과하기 전에 작업이 완료되면 나머지 작업들은 취소됩니다.

ExecutorService shutdown 예시

public class ShutdownExample {

	public static void main(String[] args) {
		int i = 1;
		boolean cmd = true;
		ExecutorService executorService = Executors.newFixedThreadPool(1);
		Runnable task1 = () -> {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName());
		};
		
		while(i < 10) {
			try {
				executorService.execute(task1);
			} catch(RejectedExecutionException e) {
				System.out.println(e.getMessage());
			}
			i++;
			if(i > 5 && cmd) {
				cmd = false;
				System.out.println("shutdown 호출" );
				executorService.shutdown();
			}
		}

	}

}

i가 5가 넘어가면 shutdown이 호출되면서 shutdown 상태일때 execute를 실행하기 때문에 RejectedExecutionException 메시지가 나옵니다. 그리고 1초 간격으로 5번 task1이 실행됩니다.

 

ExecutorService shutdownNow 예시

public class ShutdownNowExample {

	public static void main(String[] args) {
		int i = 1;
		boolean cmd = true;
		ExecutorService executorService = Executors.newFixedThreadPool(1);
		Runnable task1 = () -> {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				System.out.println(e.getMessage());
			}
			System.out.println(Thread.currentThread().getName());
		};
		
		while(i < 10) {
			try {
				executorService.execute(task1);
			} catch(RejectedExecutionException e) {
				System.out.println(e.getMessage());
			}
			i++;
			if(i > 5 && cmd) {
				cmd = false;
				System.out.println("shutdown 호출" );
				executorService.shutdownNow();
			}
		}
	}

}

i가 5가 넘어가면 shutdown이 호출되면서 shutdown 상태일때 execute를 실행하기 때문에 RejectedExecutionException 메시지가 나옵니다.  그리고 실행중인 작업은 반환이 되고 대기 중인 작업들은 종료됩니다.

 

ExecutorService isShutdown & isTerminated 예시

public class IsShutdownExample {

	public static void main(String[] args) throws InterruptedException {
		int i = 1;
		boolean cmd = true;
		ExecutorService executorService = Executors.newFixedThreadPool(1);
		Runnable task1 = () -> {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName());
		};
		
		while(i < 10) {
			try {
				executorService.execute(task1);
			} catch(RejectedExecutionException e) {
				System.out.println(e.getMessage());
			}
			i++;
			if(i > 5 && cmd) {
				cmd = false;
				System.out.println("shutdown 호출" );
				executorService.shutdown();
				System.out.println("isShutdown:" + executorService.isShutdown());
				System.out.println("isTerminated:" + executorService.isTerminated());
			}
		}
		
		Thread.sleep(6000);
		System.out.println("isTerminated:" + executorService.isTerminated());
	}

}

shutdown이 호출되고 나서 isShutdown은 true가 나오지만 작업이 완료가 되지는 않았기때문에 isTerminated는 false가 나옵니다. 6초 뒤에 isTerminated를 실행하면 모든 작업이 끝난 뒤라서 true가 나옵니다.

 

ExecutorService awaitTermination 예시

public class AwaitTerminationExample {

	public static void main(String[] args) throws InterruptedException {
		int i = 1;
		boolean cmd = true;
		ExecutorService executorService = Executors.newFixedThreadPool(1);
		Runnable task1 = () -> {
			System.out.println(Thread.currentThread().getName());
		};
		
		while(i < 10) {
			try {
				executorService.execute(task1);
			} catch(RejectedExecutionException e) {
				System.out.println(e.getMessage());
			}
			i++;
			if(i > 5 && cmd) {
				cmd = false;
				System.out.println("shutdown 호출" );
				executorService.awaitTermination(5, TimeUnit.SECONDS);
			}
		}
	}

}

task1이 실행되다가 i가 5가 넘어가면 작업이 아직 종료되지 않았기 때문에 awaitTermination 5초뒤에 넘어가서 실행됩니다.

만약 shutdown을 하고 awaitTermination를 하면 종료가 되었기 때문에 바로 넘어갑니다.

 

ExecutorService submit 예시

public class SubmitExample {

	public static void main(String[] args) throws InterruptedException, ExecutionException {
		ExecutorService executorService = Executors.newFixedThreadPool(1);
		Callable<String> task1 = () -> {
			System.out.println(Thread.currentThread().getName());
			return "hello";
		};
		Future<String> result1 = executorService.submit(task1);
		System.out.println(result1.get());
		Runnable task2 = () -> System.out.println(Thread.currentThread().getName());
		String runResult = "hi";
		Future<String> result2 = executorService.submit(task2, runResult);
		System.out.println(result2.get());
		Future<?> result3 = executorService.submit(task2);
		System.out.println(result3.get());
		
		executorService.shutdown();
	}

}

result1은 hello가 리턴되고 result2은 hi가 리턴되고 result3는 null이 리턴이 됩니다.

 

ExecutorService invokeAll & invokeAny 예시

public class InvokeExample {

	public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
		
		ExecutorService executorService = Executors.newFixedThreadPool(1);
		
		Callable<String> task = () -> {
			Thread.sleep(1000);
			return "hello";
		};
		
		List<Callable> list = new ArrayList<>();
		
		for(int j = 0; j < 5 ; j++) {
			list.add(task);
		}
		
		List<Future<String>> result1 = executorService.invokeAll((Collection) list);
		for(Future<String> item : result1) {
			System.out.println("result1:" + item.get());
		}
		
		List<Future<String>> result2 = executorService.invokeAll((Collection) list, 3, TimeUnit.SECONDS);
		for(Future<String> item : result2) {
			try {
				System.out.println("result2:" + item.get());
			} catch(CancellationException e) {
				
			}
		}
		
		String result3 = (String) executorService.invokeAny((Collection) list);
		System.out.println("result3:" + result3);
		
		String result4 = (String) executorService.invokeAny((Collection) list, 3, TimeUnit.SECONDS);
		System.out.println("result4:" + result4);
		
		executorService.shutdown();
	}

}

result1에서는 invokeAll이기 때문에 모든 작업이 완료된 뒤 반환이 되기 때문에 5초가 걸립니다.

result2에서는 3초 제한이 있는 invokeAll이기 때문에 3초가 되면 나머지 작업은 취소하고 반환됩니다.

result3에서는 invokeAny이기 때문에 하나의 성공된 작업이 나오면 그 작업만 반환됩니다.

result4에서는 3초 제한이 있는 invokeAny이기 때문에 1초뒤 성공된 작업이 나오면 그 작업만 반환됩니다.