[Java] 쓰레드 Thread

Bam·2024년 3월 16일
0

Java

목록 보기
72/98
post-thumbnail

프로세스와 쓰레드

프로세스(Process)실행 중인 프로그램이라고 할 수 있습니다. 프로그램을 실행하면 운영체제는 프로그램 실행에 필요한 자원들을 프로그램에 할당하면 프로세스가 되는 것이 기본적인 프로그램 실행의 구조입니다.

이때 프로세스에는 메모리, 데이터와 같은 자원 그리고 쓰레드(Thread)로 구성되어 있습니다. 여기서 쓰레드가 프로세스의 자원을 활용해서 실제로 작업을 수행하게 됩니다.

그래서 하나의 프로세스에는 하나 이상의 쓰레드가 존재하게 됩니다. 이때 쓰레드가 하나면 싱글 쓰레드, 둘 이상이라면 멀티 쓰레드(멀티 쓰레드 프로세스)라고 부릅니다.

요약하자면 프로세스는 작업장, 쓰레드는 일꾼이라고 할 수 있습니다.

멀티 쓰레드

멀티 쓰레드를 우리가 볼 때는 CPU가 한 번에 여러 작업을 하는 것 처럼 보입니다. 하지만 CPU는 한 번에 하나의 작업만을 처리할 수 있기 때문에 실제로는 엄청 짧은 시간에 여러 작업을 번갈아가면서 수행하고 있습니다.

멀티 쓰레드의 장점은 다음과 같습니다.

  • 자원을 효율적으로 이용할 수 있다.
  • CPU의 사용률을 높인다.
  • 사용자에 대한 응답성이 높아진다.
  • 작업 분리로 코드가 간결해진다.

멀티 쓰레딩을 이용한 대표적인 사례가 카카오톡과 같은 메신저입니다. 우리가 사진을 보내거나 누군가 보낸 사진을 다운로드 받으면서 채팅을 보낼 수 있죠? 이 이유가 멀티 쓰레드로 구현되었기 때문입니다. 만약 싱글 쓰레드 메신저였다면 다운로드하는 동안엔 채팅을 할 수가 없을 것 입니다.

물론 멀티 쓰레드가 장점이 많지만 단점이 없는 것은 아닙니다. 프로그래머가 코드를 작성할 때 Deadlock(교착 상태), 동기화 등을 생각하면서 작성하지 않으면 문제가 발생합니다.

Deadlock(교착 상태)
두 개의 쓰레드가 서로의 자원을 사용하려고 기다리는 동안 작업이 진행되지 않는 상태


자바의 메인 쓰레드

자바 프로그램은 main Threadmain()을 호출하면서 실행됩니다. 그리고 main()내부의 작업을 수행하다가 main()의 마지막이나 return에 도달하면 프로그램의 실행이 종료되게 됩니다.

이때 main에서 필요에 따라 추가적인 쓰레드를 생성하고 실행시킬 수 있습니다. 기존 싱글 쓰레드에서는 하나의 쓰레드가 종료되면 프로세스의 실행 종료로 이어졌지만, 멀티 쓰레드에서는 메인 쓰레드가 종료되어도 다른 쓰레드가 아직 실행 중이라면 프로세스가 종료되지 않습니다.


쓰레드 생성

자바에서 쓰레드를 구현하고 생성하는 방법에는 Thread 클래스를 상속하거나 Runnable 인터페이스를 구현하는 두 가지 방법이 있습니다.

두 방법 모두 결국에는 상속/구현을 하고 run()이라는 메소드를 정의해서 사용하는 것인데요. Thread 클래스를 상속하는 경우 다른 클래스를 상속받을 수 없기 때문에 일반적으로 Runnable 인터페이스를 구현하는 방식을 많이 사용합니다.

Thread 클래스 상속

먼저 Thread 클래스를 상속하는 방법부터 소개드리도록 하겠습니다.

public class 쓰레드이름 extends Thread {	//쓰레드 구현
	@Override
    public void run() {
    	//쓰레드 실행 코드
    }
}

Thread 변수명 = new 쓰레드이름();	//쓰레드 생성
변수명.start();	//쓰레드 실행

다음 코드는 실제로 쓰레드의 동작을 알아보기 위한 코드입니다.

public class ExtendsThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("ExtendsThread 작업 실행");

            try {	//멀티 쓰레드를 확인하기 위한 0.5초 딜레이
                Thread.sleep(500);
            }
            catch (Exception e) {
                System.err.println(e);
            }
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Thread thread = new ExtendsThread();	//쓰레드 생성

        thread.start();	//쓰레드 호출

        for (int i = 0; i < 5; i++) {
            System.out.println("main 쓰레드 작업 실행");

            try {	//멀티 쓰레드를 확인하기 위한 0.5초 딜레이
                Thread.sleep(500);
            }
            catch (Exception e) {
                System.err.println(e);
            }
        }
    }
}

main 쓰레드가 먼저 5번 출력되었지만 종료되지 않고 다른 쓰레드의 종료까지 기다린다는 것을 확인해볼 수 있습니다.

Runnable 인터페이스 구현

Runnable 인터페이스를 구현하는 방식 또한 인터페이스를 implements한 뒤에 run()을 구현하는 방식으로 Thread 클래스 상속과 동일하게 구현합니다. 다만, 쓰레드를 생성하는 과정이 조금 다르기 때문에 그 부분을 집중해서 봐주세요.

public class 쓰레드이름 implements Runnable {	//쓰레드 구현
	@Override
    public void run() {
    	//쓰레드 실행 코드
    }
}

Runnable Runnable객체명 = new 쓰레드이름();
Thread 변수명 = new Thread(Runnable객체명);	//쓰레드 생성

변수명.start(); //쓰레드 실행

쓰레드 생성 과정을 한 줄로 만들수도 있습니다.

Thread 변수명 = new Thread(new 쓰레드이름());

그러면 실제로 Runnable을 구현해서 쓰레드 작업을 확인해보겠습니다. 내용은 방금 전에 사용했던 코드를 Runnable 구현으로 바꿔줬습니다.

public class ImplementsRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("ImplementsRunnable 작업 실행");

            try {
                Thread.sleep(500);
            }
            catch (Exception e) {
                System.err.println(e);
            }
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(new ImplementsRunnable());

        thread.start();

        for (int i = 0; i < 5; i++) {
            System.out.println("main 쓰레드 작업 실행");

            try {
                Thread.sleep(500);
            }
            catch (Exception e) {
                System.err.println(e);
            }
        }
    }
}


오늘은 간단히 프로세스와 쓰레드의 개념, 자바에서 쓰레드를 생성하는 방법에 대해서만 알아봤습니다. 다음 두 개의 포스트를 통해서 쓰레드르 활용하는 방법에 대해서 조금 더알아볼 예정입니다.

0개의 댓글