-
[Thread] Runnable and Callable in Java๊ฐ๋ฐ/Java 2022. 4. 25. 15:31
Java ์ด๊ธฐ๋ถํฐ ๋ฉํฐ์ค๋ ๋ฉ์ ์ธ์ด์ ์ฃผ์ ์ธก๋ฉด์ด์๋ค.
Runnable ์ ๋ค์ค ์ค๋ ๋ ์์ ์ ๋ํ๋ด๊ธฐ ์ํด ์ ๊ณต๋๋ ํต์ฌ ์ธํฐํ์ด์ค์ด๊ณ ,
Callable ์ Java 1.5์ ์ถ๊ฐ๋Runnable์ ๊ฐ์ ๋ ๋ฒ์ ์ด๋ค.
๋ ์ธํฐํ์ด์ค ๋ชจ๋ ์ฌ๋ฌ ์ค๋ ๋์์ ์คํํ ์ ์๋ ์์ ์ ๋ํ๋ด๋๋ก ์ค๊ณ๋์๋ค.
ํ์ง๋ง, Runnable์ Threadํด๋์ค ๋๋ExecutorService ๋ฅผ ์ฌ์ฉํ์ฌ ์คํํ ์ ์๋ ๋ฐ๋ฉด Callable ์ ํ์๋ฅผ ์ฌ์ฉํด์๋ง ์คํํ ์ ์๋ค.
๊ทธ๋ผ ์ฐจ์ด์ ์ ์์ธํ ์ดํด๋ณด์.
Runnable
๐ก๋ฐํ ๊ฐ
Runnable ์ธํฐํ์ด์ค ๋ ๊ธฐ๋ฅ์ ์ธํฐํ์ด์ค์ด๋ฉฐ ๋งค๊ฐ๋ณ์๋ฅผ ํ์ฉํ์ง ์๊ณ ๊ฐ์ ๋ฐํํ์ง ์๋ ๋จ์ผ run() ๋ฉ์๋๊ฐ ์๋ค. ์ค๋ ๋ ์คํ์ ๊ฒฐ๊ณผ๋ฅผ ์ฐพ์ง ์๋ ์ํฉ์ ์ ํฉํ๋ค.
๐ก์คํ ๋ฐฉ์
1) Thread ํด๋์ค
package e.thread; public class RunnableSample implements Runnable{ @Override public void run() { System.out.println("This is RunnableSample's run() method."); } }
package e.thread; public class RunThreads { public static void main(String[] args) { RunThreads threads = new RunThreads(); threads.runBasic(); } public void runBasic() { //Runnable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ RunnableSample ํด๋์ค๋ฅผ ์ฐ๋ ๋๋ก ๋ฐ๋ก ์์ํ ์๋ ์๋ค. //๋ฐ๋ผ์, ์ด์ ๊ฐ์ด Thread ํด๋์ค์ ์์ฑ์์ ํด๋น ๊ฐ์ฒด๋ฅผ ์ถ๊ฐํ์ฌ ์์ํด์ฃผ์ด์ผ๋ง ํ๋ค. RunnableSample runnable = new RunnableSample(); new Thread(runnable).start(); } }
๋จผ์ run()์ด๋ผ๋ ๋ฉ์๋๋ ์ฐ๋ ๋๊ฐ ์ํ๋๋ ์ฐ๋ฆฌ๊ฐ ๊ตฌํํ๋ ๋ฉ์๋๋ฅผ ๋งํ๊ณ , start()๋ผ๋ ๋ฉ์๋๋ ์ฐ๋ ๋๋ฅผ ์์ํ๋ ๋ฉ์๋๋ค.
์์ ์ฝ๋์์์ฒ๋ผ Runnable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ๊ฒฝ์ฐ์๋ Thread ํด๋์ค์ ์์ฑ์์ ํด๋น ๊ฐ์ฒด๋ฅผ ์ถ๊ฐํ์ฌ ์์ํด
์ฃผ๋ฉด ๋๋ค.
Runnable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ ๊ฒฝ์ฐ์, Thread ํด๋์ค๋ฅผ ํ์ฅํ๋ ๊ฒฝ์ฐ์ ๋ํ ๋น๊ต ์ด์ผ๊ธฐ๋ ์๋์ ๊ฒ์๊ธ๋ก ์ด๋ํด์ ๋ณด์.
https://mong-dev.tistory.com/17
2) ExecutorService
public interface Runnable { public void run(); }
public class EventLoggingTask implements Runnable{ private Logger logger = LoggerFactory.getLogger(EventLoggingTask.class); @Override public void run() { logger.info("Message"); } }
public void executeTask() { // Executors์ ํฉํ ๋ฆฌ ๋ฉ์๋๋ฅผ ํธ์ถํด ์ฑ๊ธ์ฐ๋ ๋์ธ Executor๋ฅผ ์์ฑํจ executorService = Executors.new SingleThreadExecutor(); // executor์ Task ์ธ์คํด์ค๋ฅผ ์ ์ถ(submit)ํด์ ์คํ์ ์์ฒญํจ Future future = executorService.submit(new EventLoggingTask()); // ์ข ๋ฃ executorService.shutdown(); }
์ด ์์์ ์ค๋ ๋๋ ํ์์ ๋ฉ์์ง๋ฅผ ์ฝ๊ณ ๋ก๊ทธ ํ์ผ์ ๊ธฐ๋กํ๋๋ฐ, ์์ ์์ ๋ฐํ๋ ๊ฐ์ ์๋ค. ๋ง์ง๋ง์ executeTask() ๋ฉ์๋์์ ๋ณด๋ฏ์ด ExecutorService๋ฅผ ์ฌ์ฉํ์ฌ ์์ํ ์ ์๋ค. ์ด ๊ฒฝ์ฐ Future ๊ฐ์ฒด๋ ์ด๋ค ๊ฐ๋ ๋ณด์ ํ์ง ์๋๋ค.
Callable
๐ก๋ฐํ ๊ฐ
Callable ์ธํฐํ์ด์ค๋ ์ ๋ค๋ฆญ ๊ฐ V๋ฅผ ๋ฐํํ๋ ๋จ์ผ call() ๋ฉ์๋๋ฅผ ํฌํจ ํ๋ ์ผ๋ฐ ์ธํฐํ์ด์ค์ด๋ค.
๐ก์คํ ๋ฐฉ์
1) Only ExecutorService
public interface Callable<V> { V call() throws Exception; }
์ซ์์ ๊ณ์น์ ๊ณ์ฐํ๋ ๋ฐฉ๋ฒ์ call()์ ๊ตฌํํ์๋ค. ๋ฉ์๋ ์ ์ธ๋ถ์ throws๋ก ์์ธ์ฒ๋ฆฌ๊ฐ ๋์ด์๋๋ฐ, ์ด๋ถ๋ถ์ ๋ฐ๋ก ๋ค์ ๋จ๋ฝ์์ ์ดํด๋ณผ ์์ ์ด๋ ์คํ ๋ฐฉ์๋ง ๋จผ์ ๋ณด๋๋ก ํ๋ค.
public class FactorialTask implements Callable<Integer> { int number; // standard constructors public Integer call() throws InvalidParamaterException { int fact = 1; // ... for(int count = number; count > 1; count--) { fact = fact * count; } return fact; } }
@Test public void whenTaskSubmitted_ThenFutureResultObtained(){ FactorialTask task = new FactorialTask(5); Future<Integer> future = executorService.submit(task); assertEquals(120, future.get().intValue()); }
์์ ๋งํ๋ฏ์ด, ์ฌ๊ธฐ์ ์ค์ํ ํฌ์ธํธ๋ call() ๋ฉ์๋ ์ ๊ฒฐ๊ณผ๋ Future ๊ฐ์ฒด ๋ด์์ ๋ฐํ์ด ๋๋ค๋ ๊ฒ์ด๋ค.
๐ก์์ธ์ฒ๋ฆฌ
Runnable์ ๊ฒฝ์ฐ์๋ ๋ฉ์๋ ์ ์ธ์, "throws" ์ ์ด ์ง์ ๋์ด ์์ง ์๊ธฐ ๋๋ฌธ์ ์ถ๊ฐ๋ก ๊ฒ์ฌ๋ ์์ธ๋ฅผ ์ ํํ ๋ฐฉ๋ฒ์ด ์๋๋ฐ ๋ฐํด, Callable์ call() ๋ฉ์๋์๋ "throws Exception" ์ ์ด ํฌํจ๋์ด ์์ผ๋ฏ๋ก checked ์์ธ๋ฅผ ๋ ์ฝ๊ฒ ์ ํํ ์ ์๋ค.
โ call()์ run()์ฒ๋ผ ์ค๋ฒ๋ผ์ด๋ ์ ๊ตฌํ๋ถ์ ๋ก์ง์ ๊ฐ๋ณ ์ฐ๋ ๋๋ฅผ ํตํด ์คํ์ํจ๋ค๋ ์ ์ ๊ฐ์ง๋ง, ๋ฐํ๊ฐ์ด ์์ด์ผํ๊ณ ๋ฐํ์ ์คํจํ๋ฉด ์์ธ๋ฅผ ๋์ง๊ฒ ํ ์ ์๋ค. ๋ฐ์ ๊ฐ๋ฅํ ์์ธ๋ฅผ ๋ฏธ๋ฆฌ ์ ์ธํ๋ฉด ์ธ๋ถ์์ ํธ์ถ ์ ์์ธ ์ฒ๋ฆฌ๋ฅผ ๊ฐ์ ํ ์ ์๊ธฐ ๋๋ฌธ์ ์์ ์ฑ์ ์ธก๋ฉด์์ ํฐ ์ฅ์ ์ด๋ค.
๋ค์ ์ฝ๋๋ฅผ ๋ณด์.
public class FactorialTask implements Callable<Integer> { // ... public Integer call() throws InvalidParamaterException { if(number < 0) { throw new InvalidParamaterException("Number should be positive"); } // ... } }
@Test(expected = ExecutionException.class) public void whenException_ThenCallableThrowsIt() { FactorialCallableTask task = new FactorialCallableTask(-5); Future<Integer> future = executorService.submit(task); Integer result = future.get().intValue(); }
ExecutorService๋ฅผ ์ฌ์ฉํ์ฌ Callable์ ์คํํ๋ ๊ฒฝ์ฐ, Future.get()๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ํ์ธํ ์ ์๋ Future๊ฐ์ฒด์ ์์ธ๊ฐ ์์ง๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์๋ ์์ธ๋ฅผ ๋ํ ํ๋ ExecutionException์ด ๋ฐ์ํ๋ค.
์์ ํ ์คํธ์์ ์ ํจํ์ง ์์ ์ซ์๋ฅผ ์ ๋ฌํ ๋, ExecutionException์ด ๋ฐ์ํ๋ค. ์ด ์์ธ ๊ฐ์ฒด์ ๋ํด getCause() ๋ฉ์๋๋ฅผ ํธ์ถ ํ์ฌ ์๋ ํ์ธ๋ ์์ธ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค.
Future ํด๋์ค์ get() ๋ฉ์๋๋ฅผ ํธ์ถํ์ง ์์ผ๋ฉด call ( )๋ฉ์๋์์ throw๋ ์์ธ๊ฐ ๋ค์ ๋ณด๊ณ ๋์ง ์๊ณ , ์์ ์ ์ฌ์ ํ โโ์๋ฃ๋ ๊ฒ์ผ๋ก ํ์๋๋ค. ์๋ ์ฝ๋๋ get()๋ฉ์๋๋ฅผ ํธ์ถํ์ง ์์ ๊ฒฝ์ฐ์ด๋ค.
@Test public void whenException_ThenCallableDoesntThrowsItIfGetIsNotCalled(){ FactorialCallableTask task = new FactorialCallableTask(-5); Future<Integer> future = executorService.submit(task); assertEquals(false, future.isDone()); }
์์ ํ ์คํธ๋ FactorialCallableTask์ ๋ํ ๋งค๊ฐ๋ณ์์ ์์ ๊ฐ์ ๋ํ ์์ธ๋ฅผ throwํ๋๋ผ๋ ์ฑ๊ณต์ ์ผ๋ก ํต๊ณผํ๋ค.
์ ๋ฆฌ
Runnable Callable ๋์ JDK 1.0 JDK 1.5 ํจํค์ง java.lang java.util.concurrent ๋ณด์ ๋ฉ์๋ run() call() ๋ฉ์๋ ๋ฐํ๊ฐ ์์ ์์ ๋ฉ์๋ ์์ธ ์ ์ธ ๋ถ๊ฐ๋ฅ ๊ฐ๋ฅ ์คํ ๋ฐฉ๋ฒ Thread ๋๋ ExecutionService ExecutionService ๐Reference
- ์๋ฐ์ ์
- https://www.baeldung.com/java-runnable-callable
- https://jvnlee.tistory.com/8'๊ฐ๋ฐ > Java' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ