💡 하나의 새로운 프로세스를 생성하는 것보다 하나의 새로운 쓰레드를 생성하는 것이 더 적은 비용이 든다.
자바가 멀티쓰레드를 지원하며 1990년대말 CGI (단일쓰레드만 제공)를 사용하던 웹이 자바로 많이 넘어왔다.
CPU의 코어가 한 번에 단 한나의 작업만 수행할 수 있으므로, 실제로 동시에 처리되는 작업의 개수는 코어의 개수와 일치한다.
그러나 처리해야하는 쓰레드의 수는 언제나 코어의 개수보다 훨씬 많기 때문에 각 코어가 아주 짧은 시간 동안 여러 작업을 번갈아 가며 수행함으로써 여러 작업들이 모두 동시에 수행되는 것처럼 보이게 한다.
그래서 프로세스의 성능이 단순히 쓰레드의 개수에 비례하는 것은 아니며, 하나의 쓰레드를 가진 프로세스보다 두 개의 쓰레드를 가진 프로세스가 오히려 더 낮은 성능을 보일 수도 있다.
여러사용자에게 서비스를 해주는 서버 프로그램의 경우 멀티쓰레드로 작성하는 것은 필수적이어서 하나의 서버 프로세스가 여러 개의 쓰레드를 생성해서 쓰레드와 사용자의 요청이 일대일로 처리되도록 프로그래밍해야 한다.
동기화 (synchronization)에 주의해야 한다.
교착상태 (dead-lock)가 발생하지 않도록 주의해야 한다.
각 쓰레드가 효율적으로 고르게 실행될 수 있게 해야한다.
→ 프로그래밍할 때 고려해야 할 사항들이 많다.
“멀티쓰레드를 짜는 것이 어려운 것이 아니라 효율적으로 돌아가게 짜는 것이 어렵다.”
자바에서는 쉽게 멀티쓰레드를 구현할 수 있도록 미리 작성된 클래스들을 제공하고 있기 때문에 기본 개념만 알아도 도움이 된다.
구현하는 방법은 두 가지가 있고, 어느 쪽이든 큰 차이는 없다. 하지만 상속을 받게되면 다른 클래스를 상속받을 수 없기 때문에 인터페이스를 구현하는 방법이 일반적이다.
1) Thread 클래스를 상속 : 단일 상속이라 다른 클래스를 상속할 수 없다
class MyThread extends Thread {
public void run(){
/* 쓰레드가 수행할 작업 내용 */
}
}
MyThread t = new MyThread();
t.start()
2) ✅ Runnable 인터페이스를 구현 → better!
class MyThread implements Runnable {
public void run(){ //Runnable 인터페이스의 추상메서드 run()을 구현한다.
/* 쓰레드가 수행할 작업 내용 */
}
}
Runnable r = new MyThread();
Thread t = new Thread(r);
//Thread t = new Thread(new MyThread());
t.start();
start()
:start()
를 두 번 이상 호출하면 IllegalThreadStateException
발생)1. main 메서드에서 쓰레드의 start()를 호출한다.
2. start()는 새로운 쓰레드를 생성하고, 쓰레드가 작업하는데 사용될 호출스택을 생성한다.
3. 새로 생성된 호출스택에 run()이 호출되어, 쓰레드가 독립된 공간에서 작업을 수행한다.
4. 이제는 호출스택이 2개이므로 스케줄러가 정한 순서에 의해서 번갈아 가면서 실행된다.
- run()을 호출하면 그냥 main에서 run()이 실행되는 싱글쓰레드가 된다.
main 메서드의 코드를 수행하는 쓰레드
실행 중인 사용자 쓰레드가 하나도 없을 때 프로그램은 종료된다.
메인 쓰레드가 끝나도 실행 중인 쓰레드가 있다면 프로그램은 종료되지 않는다.
두 개의 쓰레드로 작업한 시간이 싱글쓰레드로 작업한 시간보다 더 걸리게 되는데 그 이유는 쓰레드간의 작업 전환 (context switching)에 시간이 걸리기 때문이다.
예제에서는 화면 (console)이라는 자원을 놓고 두 쓰레드가 경쟁하게 된다. 한 쓰레드가 화면에 출력하고 있는 동안 다른 쓰레드는 출력이 끝나기를 기다려야하기 때문이다.
하지만, 멀티쓰레드를 사용하면 아래와 같은 장점이 있다
1) 여러 작업을 동시에 처리할 수 있다
2) 자원을 효율적으로 쓸 수 있다.
//싱글스레드 : 사용자 입력을 기다리는 구간이 생긴다(I/O blocking)
class ThreadEx6{
public static void main(String[] args){
String input = JOptionPane.showInputDialog("아무 값이나 입력하세요.");
System.out.println("입력하신 값은 " + input + "입니다.");
for (int i = 10; i > 0; i--){
System.out.println(i);
try {Thread.sleep(1000);} catch (Exception e) {}
}
}
}
//멀티스레드 : 사용자가 입력을 마치지 않아도 화면에 숫자가 출력된다.
class ThreadEx7{
public static void main(String[] args){
ThreadEx7_1 th1 = new ThreadEx7_1();
th1.start();
String input = JOptionPane.showInputDialog("아무 값이나 입력하세요.");
System.out.println("입력하신 값은 " + input + "입니다.");
}
}
class ThreadEx7_1 extends Thread{
public void run(){
for (int i = 10; i > 0; i--){
System.out.println(i);
try {sleep(1000);} catch (Exception e) {}
}
}
}