자바 Thread

HyeonWoo·2021년 1월 13일
0

Java

목록 보기
7/12
post-thumbnail

최근의 OS는 멀티 태스킹을 지원하지 않는게 없다.
멀티 태스킹은 두 가지 이상의 작업을 동시에 하는 것을 말함.
예를 들어, 컴퓨터로 음악을 들으며, 웹서핑을 하는 것이다.

실제로 동시에 처리될 수 있는 프로세스의 개수는 CPU 코어의 개수와 동일한데, 이보다 많은 개수의 프로세스가 존재하기 때문에 모두 함께 동시에 처리될 수는 없다.

각 코어들은 아주 짧은 시간 동안 여러 프로세스를 번갈아가며 처리하는 방식을 통해 동시에 동작하는 것처럼 보이게 할 뿐이다.

멀티 쓰레딩 : 하나의 프로세스 안에서 여러 개의 스레드가 동시에 작업을 수행하는 것을 말함.

  • 스레드 : 하나의 작업 단위.

멀티 쓰레딩의 장단점

  • CPU의 사용률을 향상시킴.
  • 자원을 보다 효율적으로 사용할 수 있음.
  • 사용자에 대한 응답성이 향상됨.
  • 작업이 분리되어 코드가 간결해짐.

Java Thread

자바에서 쓰레드를 구현하는 방법은 2가지가 있다.

  1. Runnable 인터페이스 구현

  2. Thread 클래스 상속

1. Runnable 인터페이스 구현

public class MyThread implements Runnable {

    @override
    public void run(){
    	//수행코드
    }
    
 }
  • Runnable 인터페이스를 구현하므로 다른 클래스를 상속 받을 수 있다.
  • run() 메소드를 오버라이드 하면 된다.
  • 다만, start() 메소드가 없기 때문에 Runnable 인터페이스를 구현한 클래스의 객체를 만들어 Thread를 생성할 때, 생성자의 매개변수로 넘겨주고 쓰레드 객체의 start() 메소드를 수행한다.
public static void main(String[] args){

    Runnable runnable = new MyThread();
    Thread t = new Thread(runnable, "mythread);
 
 }

2. Thread 클래스 구현

public class MyThread extends Thread {

    public void run(){
    	//수행코드
    }
    
}
    
  • Thread 클래스를 상속받으면 다른 클래스를 상속받지 못한다(다중 상속 불가능)

  • run() 메소드를 직접 구현해야 한다.

  • 또한, Thread 클래스를 상속 받으면 스레드 클래스의 메소드를 바로 사용할 수 있지만, Runnable 구현의 경우 Thread 클래스의 static 메소드인 currentThread()를 호출해 현재 스레드에 대한 참조를 얻어와야만 호출 가능한다.

ex)

Thread.currentThread().getName();

Thread 실행

스레드의 실행은 run() 호출이 아닌 start() 호출로 해야 한다.


t1.start(); // 쓰레드 t1을 실행 시킨다.
t2.start(); // 쓰레드 t2를 실행 시킨다.
  • start()가 호출 되었다고 해서 바로 실행되는 것이 아니라, 일단 실행대기 상태에 있다가 자신의 차례가 되어야 실행된다.

  • 한 번 실행이 종료된 쓰레드는 다시 실행할 수 없다.

MyThread t1 = new Mythread();
t1.start();
t1.start(); // 예외 발생

----
Thread t2 = new Thread(new Mythread2());
t2.start();
t2 = new Thread(new Mythread2());
t2.start();

위에서 우리가 정의했던 메소드는 run()이다. 하지만, 실제로 스레드에게 작업을 시키려면 start()로 작업해야 한다고 한다. run() 메소드로 작업 지시를 하면 스레드가 일을 할까? 그렇지 않다. 두 메소드 모두 같은 작업을 한다. 하지만 run() 메소드를 사용한다면, 이건 스레드를 사용하는 것이 아니라 단순히 클래스에 선언된 메서드를 호출하는 것일 뿐이다.

Java에는 Call Stack이라는 것이 있다. 이 영역이 실질적인 명령어들을 담고 있는 메모리로, 하나씩 꺼내서 실행시키는 역할을 한다.

만약 동시에 두 가지 작업을 한다면, 두 개 이상의 콜 스택이 필요하게 된다. 스레드를 이용한다는 건, JVM이 다수의 콜 스택을 번갈아가며 일처리를 하고 사용자에게는 동시에 작업을 하는 것처럼 보여주는 것이다.

즉, run()메소드를 이용한다는 것은 main()의 콜 스택 하나만 이용하는 것으로 스레드 활용이 아니다. (그냥 스레드 객체의 run이라는 메소드를 호출하는 것 뿐이게 된다.)

start()메소드를 호출하면 JVM은 알아서 스레드를 위한 콜 스택을 새롭게 만들어 주고 context switching을 통해 스레드 답게 동작하도록 해준다.

결국, 우리는 새로운 콜 스택을 만들어 작업을 해야 스레드 일처리가 되는 것이기 때문에 start()메소드를 써야 하는 것이다.

start()는 스레드가 작업을 실행하는데 필요한 콜 스택을 생성한 다음 run()을 호출해서 그 스택 안에서 run()을 저장할 수 있도록 해준다.

  1. main메서드에서 쓰레드의 start()를 호출 한다.

  2. start()는 새로운 쓰레드를 생성하고, 쓰레드가 작업하는데 사용될 호출 스택을 생성한다.

  3. 새로 생성된 호출스택에 run()이 호출되어, 쓰레드가 독립된 공간에서 작업을 수행한다.

  4. 이제는 호출스택이 2개이므로 스케줄러가 정한 순서에 의해서 반갈아 가면서 실행된다.


Thread의 실행 제어

스레드는 다음과 같이 5가지의 상태를 가지고 있다.

  1. NEW : 스레드가 생성되고 아직 start()가 호출되지 않은 상태

  2. RUNNABLE : 실행 중 또는 실행 가능 상태

  3. BLOCKED : 동기화 블록에 의해 일시정지된 상태(lock이 풀릴 때까지 기다림)

  4. WAITING, TIME_WAITING: 실행 가능하지 않은 일시정지 상태

  5. TERMINATED : 스레드 작업이 종료된 상태


참고자료

profile
학습 정리, 자기 개발을 위한 블로그

0개의 댓글