스레드

sungs·2025년 7월 3일

자바

목록 보기
27/95

Thread

  • 간단히 말하자면 지휘자인 cpu 코어의 지시를 받아 코드를 실행시키는 곳이다.
  • 프로그램이 작동될 떄 한 개의 프로세스가 작동되는데, 스레드는 그 프로세스 안의 위치해 스택을 할당받는다.
  • 프로세스 안에는 여러 스레드가 있을 수 있는데 프로세스 안에 있는 힙, 데이터, 코드를 여러 프로세스가 공유하고 각 독립적인 영역을 갖는다.
  • 그리고 cpu 코어가 각 스레드마다 어떤 작업을 언제 할 지 정하는 것을 스케줄링이라 한다.
  • cpu 바운드 작업(cpu를 집중적으로 사용하는 작업 ex 연산)할 때는 cpu 코어 수보다 +1이 권장된다.
  • I/O 바운드 작업(데이커를 가져오거나 내보내는 작업 ex 네트워크)할 때는 코어 사용률에 따라 스레드를 최대한 사용할 수 있는 만큼 사용하면 좋다.
  • 스레드가 많으면 실행시킬 수 있는 게 많아져 일이 빨라진다고 생각할 수 있으나 컨텍스트 스위칭(실행 중이던 스레드가 다른 스레드로 바뀌는 것)가 자주 일어나 오히려 걸리는 시간이 더 길어질 수 있다.

자바 메모리

  • 자바 메모리는 메모리, 스택, 힙 영역으로 이루어져있다.
  • 메모리 영역: 공통 데이터 관리한다. 클래스 정보, static 영역, 런타임 상수풀이 포함된다.
  • 힙 영역: 객체하고 배열을 저장한다. 가비지 컬렉션이 주로 일어나는 곳이다.
  • 스택 영역: 실행 하나의 실행 프레임이 쌓이는 곳. 쉽게 말해서 코드를 읽어내려가면서 실행시키는 곳이다. 자바에서는 스레드를 생성해서 이 영역을 늘릴 수 있다.

스레드를 만드는 데에는 2가지 방법이 있다

1️⃣. Thred 상속

2️⃣. Runnable 구현

일단 둘의 공통점은 스레드에 이름을 따로 붙이지 않았다면 Thread-0, Thread-1... 이렇게 붙는다.
그리고 각각 독립적인 영역을 갖고 Thread를 사용할 때는 log를 사용하는 게 좋다.
그래야지 언제 실행됐는지 알 수 있다.
마지막으로 스레드 실행 순서가 보장되지 않는다.
코드를 먼저 썼다고 해서 먼저 나오지 않는다. 어느 게 먼저 실행될 지는 설정해 줄 수 있지만, 그것도 그 스레드가 높은 확률로 먼저 실행된다는 것이지 확실하게 먼저 실행시켜준다는 건 아니다.
현재 스레드에 접근하고 싶으면 currentThread(), 거기다가 이름까지 불러오고 싶으면은 getName()을 사용하면 된다.

Thread 상속

클래스에 extends Thread를 붙여 스레드로 만들어 준다.
다른 객체들처럼 객체를 만들어 사용하면 된다. 다만, 메서드를 직접 호출하는 것이 아닌 start()로 메서드로 실행해야 된다.
안 그러면 main에서 일반 메서드처럼 실행시켜 버린다.
반드시 start()로 실행해야지 Thread로 독립적인 영역을 갖게 된다.

Runnable 구현

클래스에 implements Runnable를 붙여 구현시킨다.
사용할 떄는 객체를 생성하고 또다시 Thread 스레드 이름 = new Thread(객체 이름)으로 따로 스레드를 만들어 사용한다.
이것은 번거로울 수 있으나 장점이기도 하다. Thread를 상속받을 때 생기는 불필요한 Thread 속 다른 메서드를 사용하지 않게 되기 때문이다. 더 유연하게 사용할 수 있다.
실행시킬 때는 똑같이 start()로 실행하면 된다.

Runnable 구현하는 다양한 방법

1. 중첩 정적 클래스
Runnable 클래스를 한 개의 클래스에서만 사용할 것 같으면 그 안에다가 내부 정적 클래스로 생성해도 된다.

public class OuterClassStatic {

    private String outerMessage = "안녕하세요, 바깥 클래스입니다.";

    // 정적 중첩 클래스
    static class StaticNestedRunnable implements Runnable {
        private String taskName;

        public StaticNestedRunnable(String taskName) {
            this.taskName = taskName;
        }

        @Override
        public void run() {
            // 정적 중첩 클래스는 바깥 클래스의 non-static 멤버에 직접 접근할 수 없습니다.
            // System.out.println(outerMessage); // 컴파일 에러 발생
            System.out.println(Thread.currentThread().getName() + ": " + taskName + " 실행 중 (정적 중첩 클래스)");
        }
    }

    public static void main(String[] args) {
        // 정적 중첩 클래스는 바깥 클래스 객체 생성 없이 바로 생성 가능
        StaticNestedRunnable runnable1 = new StaticNestedRunnable("작업 A");
        Thread thread1 = new Thread(runnable1);
        thread1.start();

        StaticNestedRunnable runnable2 = new StaticNestedRunnable("작업 B");
        Thread thread2 = new Thread(runnable2);
        thread2.start();
    }
}

Thread 상속 vs Runnable

일단 결론부터 말하자면 대부분 Runnable를 사용한다.
왜냐하면 Runnable이 좀더 유연하고 쓰기 편하다. 자바에서는 다중 상속은 안 되지만 다중 구현은 가능하기 때문이다.
예를 들어 이미 무언가를 상속받은 클래스에서는 Thread 상속을 사용하지 못하는 것이다.
게다가 Runnable을 사용할 경우 Thread와 따로 관리할 수 있게 된다.
하나의 Runnable 클래스로 여러 개의 스레드를 생성할 수 있는 건 덤이고.

profile
앱 개발 공부 중

0개의 댓글