Thread in Java
Java에서 Thread를 생성하는 방법은 두 가지가 존재합니다. 첫번째는 Thread Class를 상속하는 방법과 두번째는 Runnable Interface를 구현하는 방식입니다. 두 방법은 run() 함수를 작성하면 된다는 공통점을 가집니다. 하지만 Thread Class를 상속하는 방식은 Java가 다중 상속을 지원하지 않기 때문에 일반적으로 Runnable Interface를 구현하는 방식으로 Thread를 생성합니다.
Extends Thread Class
package com.donghwan.study.java.thread.extend;
// Extends Thread Class
public class StudyThread extends Thread {
@Override
public void run() {
try {
System.out.println("StudyThread " + Thread.currentThread().getName() + " start");
Thread.sleep(1_000L);
System.out.println("StudyThread " + Thread.currentThread().getName() + " end");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
Implementation Runnable Interface
package com.donghwan.study.java.thread.imple;
// Implementaion Runnable Interface
public class StudyRunnable implements Runnable {
@Override
public void run() {
try {
System.out.println("StudyRunnable " + Thread.currentThread().getName() + " start");
Thread.sleep(1_000L);
System.out.println("StudyRunnable " + Thread.currentThread().getName() + " end");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
Sample1
package com.donghwan.study.java.thread;
import com.donghwan.study.java.thread.extend.StudyThread;
import com.donghwan.study.java.thread.imple.StudyRunnable;
public class AppMain {
public static void main(String[] args) {
System.out.println("main " + Thread.currentThread().getName() + " start");
//Runnable
Runnable runnable = new StudyRunnable();
new Thread(runnable).start();
//Thread
Thread thread = new StudyThread();
thread.start();
System.out.println("main " + Thread.currentThread().getName() + " end");
}
}
// Result
main main start
main main end
StudyRunnable Thread-0 start
StudyThread Thread-1 start
StudyRunnable Thread-0 end
StudyThread Thread-1 end
위에서 결과를 알 수 있는것은 Main Thread가 존재하고 new Thread를 할 때마다 새로운 Thread가 생성되는 모습을 볼 수 있습니다. 이 때 Thread가 시작 및 종료가 되기도 전에 main() 함수가 End가 되어버렸습니다. 모든 Thread가 수행되고 난 다음 main() 함수를 종료시키고 싶은 경우에는 join을 사용하면 됩니다.
Sample2
package com.donghwan.study.java.thread;
import com.donghwan.study.java.thread.extend.StudyThread;
import com.donghwan.study.java.thread.imple.StudyRunnable;
import java.util.ArrayList;
public class AppMain {
public static void main(String[] args) {
System.out.println("main " + Thread.currentThread().getName() + " start");
ArrayList<Thread> threads = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
Thread thread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " start");
try {
Thread.sleep(1_000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + " end");
});
thread.setName("no." + i);
thread.start();
threads.add(thread);
}
threads.forEach(thread -> {
try {
System.out.println(thread.getName() + " join start");
thread.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(thread.getName() + " join end");
});
System.out.println("main " + Thread.currentThread().getName() + " end");
}
}
//Result
main main start
no.1 start
no.3 start
no.1 join start
no.2 start
no.1 end
no.2 end
no.3 end
no.1 join end
no.2 join start
no.2 join end
no.3 join start
no.3 join end
main main end
실제 코드에서는 10번까지 테스트를 하지만 결과가 너무 길어지기 때문에 1~3만 출력을 해본다면, join을 적용하게 되면 결과를 기다리게 됩니다. join()으로 어떤 Thread가 종료되길 기다리는데 예상하지 못한 이유로 종료되지 않을 수 있습니다. 이럴 때 join()을 호출하는 Thread는 무한히 기다리게 되며, 프로그램은 응답없는 상태에 빠질 수 있습니다. 이럴 때 Timeout을 적용하면, 일정 시간 기다린 후 종료되지 않았을 때 join()에서 빠져나와 다음 작업을 처리할 수 있습니다. Timeout은 join(timeout)와 같이 인자로 시간(ms)을 전달할 수 있습니다.