Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

To Dare Is To Do!

thread 본문

Java

thread

Nick_Choi 2024. 7. 5. 16:29

프로세스 (process)

- 실행중인 프로그램*

= 프로그램이 운영체제에 의해 메모리를 할당 받아 실행중인 상태

- 프로그램에 사용되는 데이터와 메모리 등의 자원, 스레드로 구성됨

 

* 프로그램 : 파일이 저장장치에 저장되어 있으나 메모리에 올라가지 않은 정적인 상태

스레드 (thread)

  • 과거 프로그램을 실행하면 시작부터 끝까지 프로세스 하나로 진행을 했으나 프로그램이 복잡해진 현재에는 프로세스 하나만으로 프로그램을 실행하기 힘든, 즉 프로그램 하나가 한 가지 작업만 하지 않는 상황이 되었다.
  • 프로세스는 운영체제의 안정성을 이유로 각자에게 할당된 메모리 내의 정보에만 접근할 수 있기 때문에 한 프로그램 안에는 여러 프로세스를 구성할 수 없다.
  • 프로세스와는 다른 더 작은 실행 단위의 개념이 요구되었고 이렇게 스레드가 탄생한다.

- 프로세스 내에서 여러 개의 실행흐름(단일, 동시적, 병렬적)을 두어 실제로 작업을 수행하는 주체

 

- 모든 프로세스는 한 개 이상의 스레드가 존재하여 작업을 수행

- 스레드 간 메모리를 공유하면 작동

- 두 개 이상의 스레드를 가지는 프로세스를 멀티 스레드 프로세스라고 함

- 프로그램이 실행되면 JVM이 동작하고 이는 자바 프로세스가 되는데 이 안에 스레드가 존재

 

모든 자바 애플리케이션은 main() 메서드가 실행하면서 시작되고 main() 메서드의 마지막 코드가 실행되거나 return문을 만나면 종료

메인 스레드는 필요에 따라 작업 스레드를 추가로 만들어 병렬적으로 코드 실행

멀티 스레드 프로세스에서 메인 스레드가 종료 되어도 작업 스레드가 진행중이라면 프로세스는 종료되지 않음

 

스레드 생성 및 실행

- 자바에서 스레드를 생성하는 방법은 Thread 클래스를 상속받거나 Runnable 인터페이스를 구현하는 것이다.

- Thread 클래스를 상속받으면 다른 클래스를 상속받을 수 없기 때문에 일반적으로 Runnable 인터페이스를 구현하는 방법으로 스레드를 생성한다.

 

class ThreadWithClass extends Thread {

	@Override
    public void run() { // run() 메서드 재정의!
        for (int i = 0; i < 5; i++) {
            System.out.println(getName()); // 현재 실행 중인 스레드의 이름을 반환함.
            try {
                Thread.sleep(10);          // 0.01초간 스레드를 멈춤.
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class ThreadWithRunnable implements Runnable {
	@Override
    public void run() { // run() 메서드 재정의!
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()); // 현재 실행 중인 스레드의 이름을 반환함.
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Thread01 {
    public static void main(String[] args){
        ThreadWithClass thread1 = new ThreadWithClass();       // Thread 클래스를 상속받는 방법
        Thread thread2 = new Thread(new ThreadWithRunnable()); // Runnable 인터페이스를 구현하는 방법
 
        thread1.start(); // 스레드의 실행
        thread2.start(); // 스레드의 실행
    }
}

 

스레드의 실행은 run() 호출이 아닌 start() 호출로 해야한다.

Java에는 콜 스택(call stack)이라는 영역이 있는데 이는 실질적인 명령어들을 담고 있는 메모리로 명령어를 하나씩 꺼내서 실행시키는 역할을 한다. 따라서 두 가지 작업을 한다면 두 개의 콜 스택이 요구된다.

이러한 상황에서 run() 메서드를 호출한다면 main()의 콜 스택 하나만 이용하게 되므로 호출만 하면 JVM이 알아서 스레드를 위한 콜 스택을 만들어주는 start() 메서드를 활용하여 생성된 스레드들이 번갈아가며 일처리를 할 수 있게 해야 한다.

 

스레드 상태

이미지 출처 : 명품 JAVA 프로그래밍 (황기태, 김효수 저)

객체 생성

NEW : 스레드 객체가 생성되었으나 스레드 대기열 큐에 올라가지 않고 start() 메소드 또 호출되지 않은 상태

 

실행 대기

RUNNABLE :

- start()가 호출되어 실행대기중인 상태, run()이 호출되면 running 상태가 됨

- 스레드가 JVM에 의해 실행되고 있거나 실행 준비되어 스케줄링을 기다리는 상태

 

일시 정지

WAITING : 일시정지 상태로 다른 스레드가 통지(nofity)할 때까지 기다리는 상태

TIMED_WAITING : 일시정지 상태로 주어진 시간 동안 기다리는 상태

BLOCKED : 일시정지 상태로 사용하고자 하는 객체의 lock이 풀릴 때까지 기다리는 상태

 

종료

TERMINATED :

- 스레드 작업이 종료된 상태

- run()이 끝나면 소멸

 

스레드 우선순위

스레드의 우선순위를 알아보기 전에 동시성과 병렬성, 스레드 스케줄링에 대해서 이해하고 넘어가보려고 한다.

 

동시성 vs 병렬성

단어의 의미만 생각하면 둘 다 동시에 작업하는 것을 의미하는 것처럼 보이지만 차이가 있다.

 

동시성

- 여러 작업을 위해 하나의 코어에서 여러 스레드를 번갈아가며 실행하는 것을 의미

- CPU가 시분할 기법을 통해 제어권을 여러 스레드에게 부여했다 뺏음으로써 동시에 실행되는 것처럼 보이게 함

 

병렬성

- 여러 작업을 위해 여러 코어에서 개별 스레드를 할당 받아 동시에 실행하는 것을 의미

- CPU가 각각 나뉘어서 각자의 일을 하여 실질적으로 동시 작업을 수행

 

스레드 스케줄링

동시성이 발휘되는 상황처럼 스레드의 개수가 코어의 수 보다 많을 경우 CPU의 제어권이 어떤 스레드에게 제공되어야 하는지 결정해야 하는 상황이 오는데 이를 결정하는 것이 스레드 스케줄링이다.

스레드들은 스케줄링에 의해 짧은 시간동안 제어권을 부여받아 자신의 run() 메서드를 실행시킨다.

자바에서는 주로 우선 순위 스케줄링과 라운드 로빈 방식의 스케줄링 기법이 사용되는데 개발자가 스레드들에게 우선순위를 부여할 수 있는 우선 순위 스케줄링과 다르게 라운드 로빈은 JVM에 의해 작동하므로 우리가 통제할 수 있는 우선순위 스케줄링을 살펴보기로 한다.

 

우선 순위 스케줄링에 의한 스레드 우선순위

스레드의 우선 순위 방식은 모든 객체가 1에서 10까지의 숫자로 우선순위를 부여받는 것으로 숫자가 클수록 높은 우선순위를 의미한다. Thread의 setPriority() 메서드를 통해서 우선순위를 설정할 수 있다.

-> 최소 우선순위 : 1 ( MIN_PRIORITY : 우선순위 1 )

최대 우선순위 : 10 ( MAX_PRIORITY : 우선순위 10 )

기본 우선순위 : 5 ( NORM_PRIORITY : 우선순위 5 )

 

public class Main {

    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            Thread thread = new CalcThread("thread" + i);
            if (i != 10) {
                thread.setPriority(Thread.MIN_PRIORITY);
            } else {
                thread.setPriority(Thread.MAX_PRIORITY);
            }
            thread.start();
        }
    }
}

public class CalcThread extends Thread {

    public CalcThread(String name) {
        setName(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 2_000_000_000; i++) {

        }
        System.out.println(getName());
    }
}

// 실행결과
thread10
thread8
thread5
thread9
thread6
thread1
thread7
thread3
thread4
thread2

우선 순위가 가장 높은 thread10이 먼저 수행되지만 나머지 스레드들은 우선순위가 같아 실행할 때마다 다른 결과가 나온다.

 

스레드 상태 제어 메서드

메소드 설명
interrupt() 일시 정지 상태의 스레드에서 InterruptedException 예외를 발생시켜, 예외 처리 코드(catch)에서 실행 대기 상태로 가거나 종료 상태로 갈 수 있도록 한다.
notify(), notifyAll() 동기화 블록 내에서 wait() 메소드에 의해 일시 정지 상태에 있는 스레드를 실행 대기 상태로 만든다.
resume() suspend() 메소드에 의해 일시 정지 상태에 있는 스레드를 실행 대기 상태로 만든다. 다만, 이 메소드는 Deprecated 되었으므로 notify()나 notifyAll()을 사용한다.
sleep() 주어진 시간동안 스레드를 일시 정지 상태로 만든다. 주어진 시간이 지나면 자동으로 실행 대기 상태가 된다.
join() join() 메소드를 호출한 스레드는 일시 정지 상태가 된다. 실행 대기 상태로 가려면, join() 메소드를 멤버로 가지는 스레드가 종료되거나, 매개 값으로 주어진 시간이 지나야 한다.
wait() 동기화 블록 내에서 스레드를 일시 정지 상태로 만든다. 매개 값으로 주어진 시간이 지나면 자동으로 실행 대기 상태가 된다. 시간이 주어지지 않으면 notify()나 notifyAll()을 호출해야만 실행 대기 상태로 갈 수 있다.
suspend() 스레드를 일시 정지 상태로 만든다. resume() 메소드를 호출하면 다시 실행 대기 상태가 된다. 다만, 이 메소드는 Deprecated 되었으므로 wait()을 사용한다.
yield() 실행 중에 우선 순위가 동일한 다른 스레드에게 실행을 양보하고 실행 대기 상태가 된다.
stop() 스레드를 즉시 종료한다. 다만, 이 메소드는 Deprecated 되었으므로 사용하지 않는 것이 좋다.

 

 

'Java' 카테고리의 다른 글

자바의 동시성 이슈 (가시성 문제, volatile)  (0) 2024.07.16
synchronized  (0) 2024.07.10
오버로딩과 오버라이딩  (0) 2024.07.04
자바 메모리 구조 (feat 자바 변수)  (0) 2024.06.12
JVM과 자바의 컴파일 과정  (0) 2024.06.11