Java/Java

스레드 (Thread)

마손리 2023. 3. 12. 22:06

프로세스(Process)와 스레드(Thread)

프로세스는 실행 중인 애플리케이션을 의미한다. 즉, 애플리케이션을 실행하면 운영체제로부터 실행에 필요한 만큼의 메모리를 할당 받아 프로세스가 됩니다.

 

프로세스는 데이터, 컴퓨터 자원, 그리고 스레드로 구성되는데, 스레드는 데이터와 애플리케이션이 확보한 자원을 활용하여 소스 코드를 실행한다. 즉, 스레드는 하나의 코드 실행 흐름이라고 볼 수 있다.

 

메인 스레드(Main thread)

자바 애플리케이션을 실행하면 가장 먼저 실행되는 메서드는 main 메서드이며, 메인 스레드가 main 메서드를 실행시켜준다. 메인 스레드는 main 메서드의 코드를 처음부터 끝까지 순차적으로 실행시키며, 코드의 끝을 만나거나 return문을 만나면 실행을 종료한다.

 

만약, 어떤 자바 애플리케이션의 소스 코드가 싱글 스레드로 작성되었다면, 그 애플리케이션이 실행되어 프로세스가 될 때 오로지 메인 스레드만 가지는 싱글 스레드 프로세스가 될 것이다. 반면, 메인 스레드에서 또 다른 스레드를 생성하여 실행시킨다면 해당 애플리케이션은 멀티 스레드로 동작하게 된다.

 

멀티 스레드(Multi-Thread)

하나의 프로세스는 여러 개의 스레드를 가질 수 있으며, 이를 멀티 스레드 프로세스라 한다. 여러 개의 스레드를 가진다는 것은 여러 스레드가 동시에 작업을 수행할 수 있음을 의미하며, 이를 멀티 스레딩이라고 한다.

 

멀티 스레딩은 하나의 애플리케이션 내에서 여러 작업을 동시에 수행하는 멀티 태스킹을 구현하는 데에 핵심적인 역할을 수행한다.

 

예를 들어, 메신저 프로그램을 사용할 때, 상대방에게 보낼 사진을 업로드하면서 동시에 메세지를 주고받을 수 있는 것이다. 이처럼 메신저 프로그램이 여러 가지 작업을 동시에 수행하려면, 작업을 동시에 실행해줄 스레드가 추가적으로 필요하게 된다.

 

스레드의 생성과 실행

메인 스레드 외에 별도의 작업 스레드를 활용한다는 것은 작업 스레드가 수행할 코드를 작성하고, 새로운 스레드를 생성하여 실행시키는 것을 의미한다.

자바는 객체지향 언어이므로 모든 자바 코드는 클래스 안에 작성되기 때문에 스레드가 수행할 코드도 클래스 내부에 작성해주어야 하며, run()이라는 메서드 내에 스레드가 처리할 작업을 작성하도록 규정되어져 있다.


run() 메서드 Runnable 인터페이스Thread 클래스정의되어져 있다. 따라서, 작업 스레드를 생성하고 실행하는 방법은 다음의 두 가지가 된다.

  • 첫 번째 방법
    • Runnable 인터페이스를 구현한 객체에서 run()을 구현하여 스레드를 생성하고 실행하는 방법
  • 두 번째 방법
    • Thread 클래스를 상속 받은 하위 클래스에서 run()을 구현하여 스레드를 생성하고 실행하는 방법

public class Main {
    public static void main(String[] args) {
        // 방법1. Runnable interface를 구현한 클래스로 스레드 생성
        ThreadWithInterface threadWithInterface = new ThreadWithInterface();
        Thread thread1 = new Thread(threadWithInterface);
//        Thread thread1 = new Thread(new ThreadInterface()); 위의 두줄의 코드를 한줄로 축약

        // 스레드 실행
        thread1.start();


        // 방법2. Thread 클래스를 상속받은 클래스로 스레드 생성 및 실행
        ThreadWithInheritance  thread2 = new ThreadWithInheritance();

        thread2.start();

        // 메인스레드의 반복문 추가
        for (int i = 0; i < 50; i++) {
            System.out.print("C");
        }

    }
}
// 방법1. Runnable을 구현하는 클래스 생성
class ThreadWithInterface implements Runnable {
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.print("A");
        }
    }
}

// 방법2. Thread를 상속받는 클래스 생성
class ThreadWithInheritance extends Thread {
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.print("B");
        }
    }
}

 

두가지 방법 모두 사용법은 같지만 Runnable을 구현하느냐 Thread를 상속하느냐의 차이이다.

 

먼저 Runnable 혹은 Thread를 상속받는 클래스를 만들어준뒤 run()메서드를 생성후 멀티스레드에서 실행될 코드를 작성한다.

그후 해당 클래스의 인스턴스를 생성하여 사용해주면된다.

 

위의 코드 실행 결과

실행결과를 보면 main스레드를 포함한 3가지 스레드동시에 처리하는 것을 확인할 수 있다. 만약 코드를 다시 실행시키면 비슷하지만 다른 결과가 나오는데 이유는 각 스레드의 실행 순서는 운영체제가 결정하기 때문이다.

(실제로 멀티스레드병렬처리처럼 보일뿐 병렬처리를 하는것은 아니다. 운영체제의 판단으로 하나의 스레드가 실행하다 중단시킨 뒤 다른 스레드를 실행하고 또 해당 스레드를 중단시킨 뒤 다른 스레드를 실행시켜 마치 병렬처리되는 것처럼 보이게 할 뿐이다.)

 

 

익명 객체를 사용하여 스레드 생성하고 실행하기

만약 간단한 코드일경우 굳이 새로운 클래스를 만들어 줄 필요는 없다.

public class Main {
    public static void main(String[] args) {
        // 방법1. Runnable interface를 구현한 클래스로 스레드 생성
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 50; i++) {
                    System.out.print("A");
                }
            }
        });
        // 스레드 실행
        thread1.start();

        // 방법2. Thread 클래스를 상속받은 클래스로 스레드 생성 및 실행
        Thread  thread2 = new Thread(){
            public void run() {
                for (int i = 0; i < 50; i++) {
                    System.out.print("B");
                }
            }
        };
        // 스레드 실행
        thread2.start();

        // 메인스레드의 반복문 추가
        for (int i = 0; i < 50; i++) {
            System.out.print("C");
        }

    }
}

위와 같이 익명객체를 이용하여 두가지 방법 모두 별도의 클래스생성 없이 스레드를 생성 및 실행 시킬수 있다.

 

스레드의 이름

메인스레드는 “main”이라는 이름을 가지며, 그 외에 추가적으로 생성한 스레드는 기본적으로 “Thread-n”이라는 이름을 가진다.

public class Main {
    public static void main(String[] args) {

        Thread thread0 = new Thread(new Runnable() {
            public void run() {
                // 현재 스레드 이름, Thread-0
                System.out.println(Thread.currentThread().getName());
            }
        });
        thread0.start();

        // 메인 스레드 이름, main
        System.out.println(Thread.currentThread().getName());

        //Thread-0의 이름 바꾸기
        thread0.setName("첫번째 멀티 스레드");
        System.out.println(thread0.getName());

        //메인 스레드 이름 바꾸기
        Thread.currentThread().setName("메인스레드");
        System.out.println(Thread.currentThread().getName());


    }
}

위의 코드 결과

위의 코드처럼 현재 스레드 혹은 특정 스레드의 이름을 확인하거나 사용자가 인식하기 쉬운 이름으로 변경도 가능하다.

 

'Java > Java' 카테고리의 다른 글

스레드의 동기화  (0) 2023.03.12
자바 가상 머신(Java Virtual Machine)  (0) 2023.03.11
스트림의 메서드  (2) 2023.03.11
스트림 (Stream)  (0) 2023.03.10
파일 입출력 (InputStream, OuputStream, FileReader, FileWriter, File)  (0) 2023.03.09