java 멀티스레드java 멀티스레드 예제자바 멀티스레드자바 스레드자바 동시성

Java 초보 개발자를 위한 멀티스레딩과 동시성

KUKJIN LEE
KUKJIN LEE
2024년 9월 12일
157

Java에서 멀티스레딩과 동시성은 Spring Boot와 같은 프레임워크를 사용할 때 중요한 역할을 합니다. 초보 개발자가 멀티스레딩을 이해하고 Spring Boot 개발에 적용하기 위해 반드시 알아야 할 필수 개념입니다.

 

1. 스레드(Thread)란?

  • 정의: 스레드는 프로그램 내에서 독립적으로 실행될 수 있는 단위입니다. 기본적으로 모든 Java 프로그램은 메인 스레드를 포함하고 있으며, 개발자는 추가적인 스레드를 생성하여 병렬 처리를 할 수 있습니다.

 

2. 스레드 생성 방법

Java에서 스레드를 생성하는 기본적인 두 가지 방법은 다음과 같습니다.

  • 클래스 상속 (extends)

class MyThread extends Thread {
    public void run() {
        System.out.println("Thread is running");
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start();  // 스레드 시작
    }
}

Thread 클래스를 상속받는 방식은 클래스가 이미 Thread를 상속받고 있기 때문에 다른 클래스를 상속할 수 없습니다. 이는 상속에 제한이 있습니다.

 

인터페이스 구현 (implements)

interface Task1 {
    void task1();
}

interface Task2 {
    void task2();
}
class MyRunnable implements Runnable, Task1, Task2 {
    public void run() {
        System.out.println("Runnable is running");
    }

    public void task1() {
        System.out.println("Task 1 is executed");
    }

    public void task2() {
        System.out.println("Task 2 is executed");
    }
}

public class Main {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable);
        t1.start();  // 스레드 시작

        // Task1과 Task2의 메서드도 호출 가능
        myRunnable.task1();
        myRunnable.task2();
    }
}

인터페이스를 구현하는 방식은 클래스가 다른 클래스를 상속받을 수 있으므로 더 큰 확장성을 가집니다. 인터페이스는 다중 구현이 가능하기 때문에 여러 기능을 동시에 구현할 수 있는 장점이 있습니다.

 

비교 요약:

  • extends: Thread 클래스를 상속받아 사용, 다른 클래스를 상속할 수 없음.

  • implements: Runnable 인터페이스를 구현하여 사용, 다른 클래스를 상속할 수 있으며 다중 인터페이스도 구현 가능.

 

3. 동기화(Synchronization)

  • 정의: 여러 스레드가 동시에 공유 자원에 접근할 때, 데이터의 일관성을 유지하기 위해 동기화가 필요합니다.

  • synchronized 키워드: 특정 메서드나 블록을 동기화하여 한 번에 하나의 스레드만 접근할 수 있게 만듭니다.

class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

사용 이유: 동기화를 통해 여러 스레드가 동시에 한 객체에 접근하여 발생할 수 있는 데이터 불일치 문제를 해결할 수 있습니다.

 

4. 스레드 상태(Thread States)

Java 스레드는 실행 중 다양한 상태를 가질 수 있습니다. 이를 이해하면 스레드의 실행 흐름을 관리하는 데 도움이 됩니다.

  • NEW: 스레드가 생성되었지만 아직 시작되지 않은 상태.

  • RUNNABLE: 스레드가 실행 중이거나 실행 준비 상태.

  • BLOCKED: 스레드가 락을 기다리며 일시 중단된 상태.

  • WAITING: 다른 스레드가 특정 작업을 완료하기를 기다리는 상태.

  • TERMINATED: 스레드 실행이 완료된 상태.

이러한 상태는 스레드 실행 중 발생할 수 있는 문제를 디버깅할 때 유용합니다.

 

class MyThread extends Thread {
    public void run() {
        try {
            // 스레드 상태를 RUNNABLE 상태로 만든 후 일시 중지
            Thread.sleep(100); // TIMED_WAITING 상태
            synchronized (this) {
                wait(); // WAITING 상태
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        MyThread t1 = new MyThread();

        // NEW 상태: 스레드를 생성했지만 시작하지 않은 상태
        System.out.println("Thread state after creation: " + t1.getState());

        t1.start();

        // RUNNABLE 상태: 스레드가 실행 중이거나 실행 준비 상태
        System.out.println("Thread state after start: " + t1.getState());

        // 잠시 대기하여 스레드가 sleep에 진입할 시간을 줌
        Thread.sleep(50);
        System.out.println("Thread state during sleep (TIMED_WAITING): " + t1.getState());

        // 스레드가 wait에 진입할 시간을 줌
        Thread.sleep(150);
        System.out.println("Thread state during wait (WAITING): " + t1.getState());

        // 스레드가 WAITING 상태일 때 notify 호출
        synchronized (t1) {
            t1.notify(); // WAITING 상태에서 깨워줌
        }

        // 스레드가 종료될 때까지 기다림
        t1.join();
        // TERMINATED 상태: 스레드 실행이 완료된 상태
        System.out.println("Thread state after termination: " + t1.getState());
    }
}

 

5. extendsimplements의 의미

  • extends (확장하다): 클래스가 다른 클래스를 확장하여 그 기능을 물려받습니다. 상속 구조에서 부모 클래스의 기능을 자식 클래스에서 사용할 수 있게 됩니다.

  • implements (구현하다): 클래스가 인터페이스를 구현하여 인터페이스에 정의된 메서드들을 실제로 구현합니다. 여러 인터페이스를 동시에 구현할 수 있어 다중 기능을 제공할 수 있습니다.

 

  • 예시:

    • extends: 기존 클래스를 확장하여 상속받는 경우.

    • implements: 인터페이스를 구현하여 새로운 기능을 클래스에 추가하는 경우.

관련 글

[SQL 입문] 필요한 데이터만 콕 집어 가져올 수 있는 WHERE 절

사용자는 테이블에 있는 모든 데이터를 다 보고 싶어 하지는 않습니다. 오히려 특정 항목에 대한 데이터만 가져오고 싶을 때가 훨씬 많습니다. 예를 들어, 어떤 사용자의 이메일이나 ID는 알고 있는데, 그 사람이 언제 우리 앱에 가입했는지 확인하고 싶다고 가정해 봅시다...

2026년 1월 27일10

SQL에서 SELECT란 무엇인가?

데이터베이스를 거대한 서류 보관함이라고 생각하면 됩니다. 보관함 안에는 수많은 데이터가 차곡차곡 쌓입니다. SELECT는 이 보관함에서 "내가 원하는 정보를 찾아줘!"라고 요청하는 명령어입니다. 이 과정을 전문 용어로 '쿼리(Query)'라고 부릅니다. &nbs...

2026년 1월 26일10

[SQL 기초] "언제 하나씩 다 넣어?" 데이터 한 번에 넣기

개발을 하다 보면 데이터베이스(DB)에 샘플 데이터를 대량으로 넣어야 할 때가 있습니다. 메뉴 100개를 추가해야 하는데 INSERT 문을 100번 쓰고 있다면? 너무 비효율적이죠! 오늘은 SQL에서 여러 데이터를 한 번에 넣는 '다중 삽입(Multiple Inse...

2026년 1월 21일13

SQL 데이터 삽입하기

데이터가 없는 데이터베이스는 연료 없는 로켓과 같습니다. 이제 'Missions' 테이블에 새로운 행(row)을 추가하는 방법을 알아보겠습니다. "삽입(Inserting)"은 '데이터 추가'를 의미합니다. 기존 Missions 테이블 ...

2026년 1월 20일12