[JAVA]Thread(쓰레드)- 2.Thread 구현과 실행 스레드의 생성과정,우선순위,쓰레드 실행주기,싱글-멀티스레드,
]저번 포스팅에서 쓰레드(Thread)가 무엇인지에 관해서 설명했다.
[JAVA]Thread(쓰레드)- 1.Thread란 무엇일까? 정의,멀티쓰레드와 멀티프로세스
-->http://gongstudyit.tistory.com/archive/20180515
이번에는 쓰레드를 구현하고 실행하는 방법 그외에 쓰레드의 우선순위, 실행주기등에 대해서 설명하려고한다.
자바에서 스레들르 만들기 위해서는 두가지 작업을 해야한다.
- 스레드 코드 작성
- JVM에게 스레드를 생성하여 스레드 코드를 실행 하도록 요청
// 1. Thread클래스를 상속
class MyThread extends Thread{
public void run() {/* 작업내용 */ } //Thread클래스의 run( ) 을 오버라이딩
}
// 2. Runnable클래스를 상속
class MyThread implements Runnable{
public void run() {/* 작업내용 */ } //Runnable인터페이스의 추상메서드 run()을 구현
}
Runnable 인터페이스는 오로지 run( ) 만 구현되어있는 간단한 인터페이스라서
구현하기 위해서는 추상메서드인 run의 몸통 { } 을 만들어주면 된다.
쓰레드 구현하는 2가지 방법의 차이는
Thread 클래스를 상속받은 경우와 Runnable 인터페이스를 구현한 경우의 인스턴스 생성 방법이 다르다.
아래 예제를 보자
public class ThreadEx1 {
public static void main(String[] args) {
ThreadEx1_1 t1 = new ThreadEx1_1(); //Thread의 자손 클래스 인스턴스 생성
Runnable r = new ThreadEx1_2(); //Runnable을 구현한 클래스의 인스턴스 생성
Thread t2 = new Thread(r); //생성자 Thread(Runnable target)
//저위의 두줄을 한줄로 간단하게 하려면..r 대신에 new ThreadEx1_2()를 넣으면 된다.
t1.start();
t2.start();
}
}
class ThreadEx1_1 extends Thread{
public void run() {
for(int i=0; i<3; i++) {
System.out.println(getName());//조상인 Thread의 getName()을 호출
}
}
}
class ThreadEx1_2 implements Runnable{
public void run() {
for(int i=0; i<3; i++) {
//Thread.currentThread() - 현재 실행중인 Thread를 반환한다.
System.out.println(Thread.currentThread().getName());//조상인 Thread의 getName()을 호출
}
}
}
<실행결과>
Thread-0 Thread-0 Thread-0 Thread-1 Thread-1 Thread-1
Runnable인터페이스를 구현한경우, 구현한 클래스의 인스턴스를 생성한 다음에 (위의 r처럼)
이 인스턴스를 Thread클래스의 생성자의 매개변수로 제공해야한다.
Thread클래스를 상속받으면, 자손 클래스에서 조상인 쓰레드 클레스의 메소드를 직접 호출할수있지만.
Runnable을 구현하면 쓰레드 클래스의 static 메서드인 current Thread( )를 호출하여 쓰레드에 대한 참조를 얻어와야만
호출이 가능하다.
그래서 위의 예제에 ThreadEx1_2에는 멤버라고는 run( )밖에 없기 때문에
'Thread.currentThread().getName()'와 같이 해야한다.
- static Thread currentThread( ) 현재 실행중인 쓰레드의 참조를 반환한다.
- String getName( ) 쓰레드의 이름을 반환한다.
이렇게 에러가 발생!!
근데 여기서 주의할거는 쓰레드 1, 쓰레드 2가 돌아간다고 가정할때 1,2의 네임을 똑같이 해주면
어떤 쓰레드가 돌아가고있는지 네임을 얻어서 콘솔창에서 찍을때 알수가없다
그러면 스레드1 스레드2의 이름은 다르게하고 스레드1을 두번 실행했을때는 어떨까??
당연한 얘기지만 Thread_Ex3에서 set해준 네임그대로 찍혀나온다.
결론은
아까 위에서 한쓰레드 대해 start( )가 한번만 호출될수있는데
만일 쓰레드 작업을 한번더 수행해야할때 새로운 쓰레드를 만들어서 start( )를 호출했는데
그때
thread -0
thread -1이라고나온건 내가 쓰레드 이름을 설정해주기 않았기때문에 그렇게 나온거라는걸 얘기하고싶었다.
맨처음 예제에서 쓰레드를 상속받아 구현한것과 러너블 인터페이스를 구현한것도 출력결과가
thread-0
thread-1이라고 나왔는데
이것도 이름을 설정해주지 않아서인데 이름을 설정하게되면 다른스레드는 서로 다른이름으로 찍히겠지만,
같은 쓰레드는 같은 이름으로 나온다는걸 (혼동할수있을거같아서) 설명했다.
<start( )와 run( )>
코드에 start( ) 와 run( ) 이 자주 등장했는데 둘에대해 설명하려고 한다.
main메서드에서 run( )을 호출하는 것은 생성된 쓰레드를 실행시키는게 아니라
-->단순히 클래스에 선언된 메서드를 호출하는거다.
반면에 start( )는 새로운 쓰레드가 작업을 실행하는데 필요한 호출스택(call stack)을 생성한 다음에
-->run( )을 호출해서, 생성된 호출스택에 run( )이 첫번째로 올라가게 한다.
뭔말이냐?? 면..!
맨위에 예시처럼 우리가 스레드를 상속받아서 구현시에 run( ) 이라는 메소드를 오버라이딩 했는데
run( )을 스레드코드 라고부른다.
스레드는 run( ) 메소드에서부터 실행을 시작하고 run( ) 이 종료하면 스레드도 종료한다.
-->(만약 run( )을 오버라이딩 하지 않으면) Thread클래스의 run( )이 실행되며 Thread의 run( )은 아무일도 하지x
단순 리턴하도록 작성되어 있어서 스레드가 바로 종료된다.
그러니까 쓰레드를 상속받아서 run()이라는 메소드를 오버라이딩해서 스레드 코드를 작성한다.
그리고 스레드 객체를 생성한거에 생명력을 가지고 실행하게 하는게 바로 start다 !!
start( )메소드를 호출함으로써 스레드를 동작시킨다.
(start( )메소드는 Thread클래스에 구현된 메소드이며 개발자가 오버라이딩하면 안된다.)
start( )메소드가 생성된 스레드 객체를 스케줄링이 가능한 상태로 전환하도록 JVM에게 지시를 한다.
이후 스케줄링에 의해서 그 스레드가 선택되면 비로소 JVM에 의해 run( )메소드가 호출되어 실행을 시작한다.!!