Thread in Java - 첫번째 이야기

lango·2022년 5월 31일
1

자바(Java)

목록 보기
2/4

쓰레드? 그냥 로딩 기다리지 않고 다른 일을 같이 할 수 있는 것 아니야?

오늘은 개발하면서 많은 고민을 던져줬던 자바 쓰레드 이야기를 해보려 한다.


Process

쓰레드를 설명하기 앞서 프로세스에 대해서 짚고 넘어가야 한다.

프로그램을 실행하면 실행에 필요한 자원을 할당 받아 프로그램이 실행된다.
실행되어 동작하고 있는 프로그램을 프로세스(Process)라고 한다.
💡 프로세스 = 데이터 + 자원 + 쓰레드

보통 하나의 프로세스는 하나의 작업을 하지만,
여러 가지 작업을 동시에 하기 위해 쓰레드를 활용한다.

실무에서 스레드의 필요성을 느끼기 전까지는 "동시에 여러 작업을 해야할 필요가 있나?" 라는 의문이 있었다. 그런데 서비스 및 솔루션을 개발하면서 하나의 작업이 완료될 때까지 기다리고 다음 작업을 시작하도록 구현하는 것이 바보같은 짓이라고 알게되는 건 그리 길지 않았다.

Thread

쓰레드(Thread)란 프로세스의 실행 단위로 한 프로세스 내에서 동작되는 여러 실행의 흐름으로 프로세스 내의 주소 공간이나 자원을 공유할 수 있다.

자바에서는 Thread 클래스, Runnable 인터페이스 두 가지 방법을 통해 쓰레드를 구현할 수 있다.
먼저 Thread 클래스 예제를 작성해보았다.

Thread Class

public class ThreadExample extends Thread {
    
    public void run() {
        System.out.println("thread is run, thread name: "+this.getName());
    }
    
    public static void main(String[] m) {
        ThreadExample threadExample = new ThreadExample();
        threadExample.setName("threadExample");
        threadExample.start();
    }
}
thread is run, thread name: threadExample

예제를 보면 ThreadExample 클래스가 Thread 클래스를 상속하였고 Thread 클래스의 run 메소드를 구현하면 ThreadExample.start() 실행시 ThreadExample 객체의 run메소드가 수행된다.

run을 구현하였는데 왜 start로 쓰레드를 실행시키나?

Thread 클래스를 상속받았기에 start 실행시 run 메소드가 수행된다.
Thread 클래스는 start 실행 시 run 메소드가 수행되도록 내부적으로 동작한다.


그런데 우리의 실무 개발환경은 쓰레드 하나만 쓰진 않을 거라고 생각한다.
쓰레드의 동작을 확인할 수 있게 여러 개 쓰레드(멀티쓰레드)를 실행해보자.

public class ThreadExample extends Thread {
    
    public void run() {
        System.out.println("thread is run, thread name: "+this.getName());
    }
    
    public static void main(String[] m) {
        ThreadExample t1 = new ThreadExample();
        ThreadExample t2 = new ThreadExample();
        ThreadExample t3 = new ThreadExample();

        t1.setName("111");
        t2.setName("222");
        t3.setName("333");

        t1.start();
        t2.start();
        t3.start();
    }
}
thread is run, thread name: 333
thread is run, thread name: 111
thread is run, thread name: 222

출력을 보면 멀티쓰레드 환경이기에 쓰레드가 동시다발적으로 생성된다.

위의 예제처럼 Thread를 상속하여 구현하면 다른 클래스를 상속할 수 없기 때문에
보통은 Runnable 인터페이스를 구현하도록 하는 방법을 주로 사용한다고 한다.

자바는 다중상속이 불가능한 특성이 존재하기에 Runnable 인터페이스로 구현한다면 다른 클래스를 상속받아서 재사용성을 높일 수 있다.

Runnable interface

Runnable interface는 Thread class와 다르게
Runnable class를 구현(implements)하는 방식이다.

위에서 작성한 Thread class 예제를 Runnable interface를 사용하는 방식으로 수정해보자.

public class RunnableExample implements Runnable {
    
    public void run() {
    	System.out.println("thread is run, thread name: " + Thread.currentThread().getName());
    }
    
    public static void main(String[] m) {
      	Thread t1 = new Thread(new RunnableExample());
        Thread t2 = new Thread(new RunnableExample());
        Thread t3 = new Thread(new RunnableExample());

        t1.setName("111");
        t2.setName("222");
        t3.setName("333");

        t1.start();
        t2.start();
        t3.start();
    }
}
thread is run, thread name: 111
thread is run, thread name: 333
thread is run, thread name: 222

Thread class와 다른점을 꼽자면 Thread를 상속(extends) 하는 것이 아닌 Runnable을 구현(implements)하도록 변경되었고, Thread의 생성자로 Runnable 인터페이스 객체를 지정하도록 변경되었다.


Thread vs Runnable

Thread class를 구현하는 것이 단순하긴 하나, 다른 클래스를 상속받을 수 없다.
반면에 Runnable interface를 구현할 경우 다른 인터페이스를 구현할 수 있으며 다른 클래스도 상속받을 수 있다. 고로 코드의 재사용성이 높고 일관성을 유지할 수 있기에 객체지향적이다.

나도 실제로 API 개발할 때 Runnable 인터페이스를 구현하는 방식으로 구현한만큼
실제로 실무에서 Runnable interface가 많이 사용되는 이유인 듯 하다.


Final..

구글링하면 거의 비스무리하게 알 수 있는 내용들이지만 적어보면서 다시금 공부하게 되는 시간이었다.

사실 이번 포스팅에 하고 싶은 이야기가 더 남아있었지만..
멀티쓰레드를 효율적으로 처리하는 것과 관련된 ExecutorService와 Executors를 통해 Thread pool를 구현해보고 이에 대한 장단점을 적어보려 했지만 생각보다 길어져 다음 포스팅에 이야기 해보려한다.





참조 문서

https://wikidocs.net/230#thread
https://gnaseel.tistory.com/21
https://aileen93.tistory.com/105
https://jongminlee0.github.io/2020/03/16/thread/
https://www.daleseo.com/java-thread-runnable/

profile
찍어 먹기보단 부어 먹기를 좋아하는 개발자

0개의 댓글