[Java] Thread
프로그램이 실행이 요청되면 메모리 공간이 할당되고, 이 메모리 공간을 기반으로 프로그램이 실행됩니다.
이렇듯 할당된 메모리 공간을 이용해 실행 중에 있는 프로그램을 Process(프로세스)라고 합니다. 보통 여태까지 해왔던 프로세스들은 main메소드의 호출을 통해 프로그램 하나만 실행해 왔습니다. 그러나 하나의 프로세스 내에서 둘 이상의 프로그램을 형성할 수도 있습니다.
class ShowThread extends Thread{
String threadName;
public ShowThread(String name) {threadName = name;}
public void run() { //Thread의 mian문인 run을 오버라이딩
for(int i = 0; i<10; i++) {
System.out.println(threadName);
try { sleep(100); // 실행흐름을 잠시 멈추는 메소드, 1/1000 * 인자값
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class myThread {
public static void main(String[] args) {
// TODO Auto-generated method stub
ShowThread threadA = new ShowThread("A");
ShowThread threadB = new ShowThread("B");
threadA.start();//run 메소드를 호출해주는 start 메소드
threadB.start();
}
/* 쓰레드 >> 프로세스내에서 프로그램의 흐름 형성하는 주체
* 두 인스턴스가 동시에 실행된다. >> 프로세서 내의 둘 이상의 프로그램 흐름을 형성할 수 있음
*/
}
실행결과 :
A
B
B
A
A
-생략-
실행결과를 보면 생성된 인스턴스 st1, st2가 동시에 실행되는 것이 보입니다.
- run 메소드 : 쓰레드는 별로의 프로그램 흐름을 구성합니다. 따라서, 쓰레드는 쓰레드만의 main 메소드를 지니는데 , 그것이 바로 run 메소드입니다.
- Thread 클래스 : Java에서 쓰레드를 인스턴스로 표현하기 때문에, 쓰레드 표현을 위한 클래스가 정의되어야 합니다. 이를 위해 Thread라는 이름의 클래스를 상속해야합니다.
- Sleep 메소드 : 실행흐름을 일시적으로 멈추는 역할을 합니다.(1/1000* 인자)
- start 메소드 : Thread 클래스에 정의되어 있으며, run 메소드를 호출해 별도의 프로그램을 실행시킵니다.
- 그럼 start 메소드 말고 run 메소드를 직접 호출시키면 어떨까요?
run 메소드를 직접 호출할 수는 있습니다. 단, run 메소드의 호출이 쓰레드 생성으로는 이어지지 않습니다. 즉, 쓰레드를 위한 메모리 공간 할당이 되지 않습니다. 그래서 start 메소드의 호출을 통해 쓰레드가 자신 만의 메모리 공간을 할당 받게 해줘야합니다.
- main메소드가 종료되어도 쓰레드는 계속 실행될까?
쓰레드는 main 메소드(사실 main 메소드도 쓰레드에 의해 실행된다.)와 별도의 흐름을 가지고 실행흐름을 이어가기 때문에 main 메소드가 종료되어도 남아있는 쓰레드가 있다면 쓰레드가 종료되어야 프로그램이 종료된다.
- 쓰레드는 정확이 무엇을 말하는 걸까?
Thread를 상속하는 인스턴스를 가리켜 쓰레드라고 하지만, 이는 엄밀히 말해서 틀린 말입니다. 쓰레드라고 불리기 위해서는 메모리 할당 공간과 다른 쓰레드들과 CPUㄹ를 나눠 쓰기 위한 각종 정보들이 등록되어여 합니다. 따라서 별도의 실행흐름을 형성하기위해서 자바 가상머신에 의해 만들어지는 모든 리소스와 각종 정보들을 총칭해서 쓰레드라 합니다.
- 쓰레드를 생성하는 방법
- 위 예시와 같이 Thread 클래스를 상속받게 하여 생성할 수 있습니다. 단 문제가 하나 존재합니다. Java에서는 한번에 한 클래스만 상속이 가능하기에 해당 클래스가 Thread 클래스 이외에 클래스를 상속 받아야 하면 Thread 클래스를 상속받을 수 없게 됩니다. 이를 위해 Java는 인터페이스의 구현을 통한 방법을 마련했습니다.
class Sum{
int num;
public Sum() {num =0;}
public void addNum(int n) { num += n;}
public int getNum() {return num;}
}
class AdderThread extends Sum implements Runnable{ //Sum 클래스를 상속받아 Runnable 인테페이스를 구현
int start , end;
public AdderThread(int s, int e) {
start = s;
end = e;
}
public void run(){
for(int i = start; i <= end; i++) {
addNum(i);
}
}
}
public class RunnableThread {
public static void main(String[] args) {
// TODO Auto-generated method stub
AdderThread at1 = new AdderThread(1,50);
AdderThread at2 = new AdderThread(51,100);
Thread tr1 = new Thread(at1);
Thread tr2 = new Thread(at2);
tr1.start();
tr2.start();
try {
tr1.join(); // join 메소드 : tr1이 참조하는 쓰레드가 멈춰야만 코드가 실행됨
tr2.join();
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("1~100까지의 합 : " + (at1.getNum()+ at2.getNum()));
}
}
Runnable 인터페이스 구현을 통해 쓰레드를 생성하였습니다. 단 주의해야할 점이 있습니다. Runnable 인터페이스를 구현하였다고 하여 start 메소드를 불러올 수는 없는니다. start 메소드는 Thread 클래스의 정의된 메소드이기 때문입니다.
따라서 Thread 클래스의 인스턴스를 만들어 Runnable 인터페이스를 구현하는 인스턴스의 참조 값을 Thread 클래스의 생성자에게 전달해야 합니다.
- join 메소드 : 다른 쓰레드의 실행완료를 기달리게 합니다. 예를 들어 tr1 쓰레드가 종료되어야 tr2 쓰레드가 실행됩니다.