[Java] Thread

나르·2021년 7월 17일
0

JAVA

목록 보기
7/18
post-thumbnail
post-custom-banner

📖 Process

실행중인 프로그램을 프로세스(Process)라고 합니다. 보통 한 개의 프로세스는 한 가지의 일을 하지만, 쓰레드를 이용하면 한 프로세스 내에서 동시에 여러 작업을 할 수 있게 됩니다.

📖 Thread

하나의 프로세스 내부에서 독립적으로 실행되는 하나의 작업 단위를 말하며, 세부적으로는 운영체제에 의해 관리되는 하나의 작업 혹은 태스크를 의미합니다.

  • 자바 main 또한 메인 스레드가 main 메소드를 실행하면서 시작하는 스레드입니다

  • main( ) 이외의 또 다른 스레드를 만들려면 Thread 클래스를 상속하거나 Runnable 인터페이스를 구현합니다.

  • 다중 스레드 작업 시에는 각 스레드 끼리 정보를 주고받을 수 있어 처리 과정의 오류를 줄일 수 있습니다.

  • 프로세스끼리는 정보를 주고받을 수 없습니다.

Runnable 상태 : 쓰레드가 실행되기위한 준비 단계. start( ) 메소드를 호출하면 run( ) 메소드에 설정된 스레드가 Runnable 상태로 진입합니다.

Running 상태 : 스케줄러에 의해 선택된 쓰레드가 실행되는 단계. run() 메서드는 JVM만이 호출 가능합니다. Runnable(준비상태)에 있는 여러 스레드 중 우선 순위를 가진 스레드가 결정되면 JVM이 자동으로 run( ) 메소드를 호출하여 스레드가 Running 상태로 진입합니다.

Dead 상태 : 스레드가 모두 실행되고 난 후 완료 상태입니다. “Done” 상태라고도 합니다.

Blocked 상태 : 쓰레드가 작업을 완수하지 못하고 잠시 작업을 멈추는 단계. wait( ) 메소드에 의해 Blocked 상태가 된 스레드는 notify( ) 메소드가 호출되면 Runnable 상태로 갑니다. sleep(시간) 메소드에 의해 Blocked 상태가 된 스레드는 지정된 시간이 지나면 Runnable 상태로 갑니다.

📘 Thread 클래스

  • JDK에서 지원하는 java.lang.Thread 제공

<Thread 생성자>

Thread()
Thread(String s)스레드 이름
Thread(Runnable r)인터페이스 객체
Thread(Runnable r, String s) 인터페이스 객체와 스레드 이름

<Thread 메소드>

Thread Method
static void sleep(long msec)
throws Interrupted Exception
msec에 지정된 밀리초 동안 대기
String getName() 스레드의 이름을 반환
void setName(String s)스레드의 이름을 s로 설정
void start()스레드를 시작 run() 메소드 호출
int getPriority() 스레드의 우선 순위를 반환
void setPriority(int p) 스레드의 우선순위를 p값으로
boolean isAlive() 스레드가 시작되었고 아직 끝나지 않았으면 true 끝났으면 false 반환
void join()
throws InterruptedException
스레드가 끝날 때 까지 대기
void run() 스레드가 실행할 부분 기술 (오버라이딩 사용)
void suspend() (deprecated)스레드가 일시정지 resume()에 의해 다시시작 할 수 있다.
void resume() (deprecated) 일시 정지된 스레드를 다시 시작.
void yield()다른 스레드에게 실행 상태를 양보하고 자신은 준비 상태로
void stop() (deprecated)스레드를 즉시 종료시킨다.

Object 클래스의 메소드

Method
wait()
wait(long millis)
동기화(synchronized) 블록 내에서 스레드를 일시 정지 상태로 만든다. 매개값으로 주어진 시간이 지나면 자동적으로 실행 대기 상태가 된다.
시간이 주어지지 않으면 notify(), notifyAll() 메소드에 의해 실행 대기 상태로 갈 수 있다.
notify()
notifyAll()
동기화 블록 내에서 wait() 메소드에 의해 일시 정지 상태에 있는 스레드를 실행 대기 상태로 만든다.

참고:

  • 스레드는 스레드마다 이름이 있어 디버깅할때 어떤 스레드가 에러를 내는지 조사할 목적으로 사용된다. default는 "Thread-n"이라는 이름으로 설정됨.
  • suspend,resume,stop 메서드는 쓰레드를 교착상태(dead-lock)에 빠트릴 수 있어 deprecated 되었다. 대신 waitnotify 메서드가 권장된다.

📘 Thread 생성

  1. Thread 클래스를 상속받는 방법
public class Test extends Thread {
    int seq;
    public Test(int seq) {
        this.seq = seq;
    }
    public void run() {
        System.out.println(this.seq+" thread start.");
        try {
            Thread.sleep(1000);  // 시작-종료 사이 1초 간격
        }catch(Exception e) {}
        System.out.println(this.seq+" thread end.");
    }

    public static void main(String[] args) {
        for(int i=0; i<10; i++) {
            Thread t = new Test(i);
            t.start();
        }
        System.out.println("main end.");
    }
}

/* result
0 thread start.
4 thread start.
6 thread start.
main end.
3 thread start.
...
8 thread end.
9 thread end.
1 thread end.
5 thread end. */

순서와 상관없이 동시에 실행됩니다. 따라서 thread 종료 이전에 main method가 종료될 수도 있습니다.

모든 쓰레드가 종료된 후에 main 메소드를 종료시키고 싶은 경우에는 Join 메소드를 사용합니다. join 메소드는 쓰레드가 종료될 때까지 기다리게 하는 메서드입니다.

쓰레드를 순차적으로 실행하고 싶다면 run 이후 바로 join을 거시면 됩니다.

public static void main(String[] args) {
        for(int i=0; i<10; i++) {
            Thread t = new Test(i);
            t.start();
            t.join();
        }
        System.out.println("main end.");
    }

  1. Runnable 인터페이스를 구현하는 방법
    (현재의 클래스가 이미 다른 클래스로부터 상속 받고 있는 경우...)
public class Test implements Runnable {
    int seq;
    public Test(int seq) {
        this.seq = seq;
    }
    public void run() {  // implement 
        System.out.println(this.seq+" thread start.");
        try {
            Thread.sleep(1000);
        }catch(Exception e) {}
        System.out.println(this.seq+" thread end.");
    }

    public static void main(String[] args) {
        ArrayList<Thread> threads = new ArrayList<Thread>();
        for(int i=0; i<10; i++) {
            Thread t = new Thread(new Test(i));
            t.start();
            threads.add(t);
        }

        for(int i=0; i<threads.size(); i++) {
            Thread t = threads.get(i);
        }
        System.out.println("main end.");
    }
}

📘 Daemon Thread

쓰레드는 사용자 쓰레드와 데몬 쓰레드 2종류가 있습니다.
지금까지 언급했던 것은 전부 사용자 쓰레드이고, 데몬 쓰레드의 경우 일반 쓰레드의 작업을 돕는 보조적인 역할을 수행하는 쓰레드를 말합니다.
보조 역할을 하는 쓰레드이므로 일반 쓰레드가 모두 종료되면 데몬 쓰레드 또한 강제적으로 종료됩니다.
데몬 쓰레드의 예로는 일정 시간마다 자동으로 수행되는 가비지 컬렉터, 자동저장, 화면 자동갱신 등이 있습니다.

쓰레드를 데몬 쓰레드로 생성시키고 싶다면 아래와 같이 setDaemon() 메서드만 실행시켜주면 됩니다

Thread thread = new Thread(new MyThread());
thread.setDaemon(true); // start 전에 해줘야함

thread.start();

📖 Multi Threading

멀티 프로세스(multi process)는 여러 개의 CPU를 사용하여 여러 프로세스를 동시에 수행하는 것을 의미합니다.
멀티 프로세스는 서로 독립적으로 실행되어, 하나의 프로세스에서 오류가 발생하더라도 다른 프로세스에 영향을 미치지 않습니다.

멀티 스레드(Multi Thread)는 하나의 프로세스 내에서 둘 이상의 스레드가 동시에 작업을 수행하는 것을 의미합니다.
하나의 프로세스 내부에 존재하기 때문에 하나의 스레드가 예외를 던지면 프로세스 하나가 아예 동작하지 않을 수 있습니다. 따라서 예외 처리 중요합니다.

장점
1. 메모리 공유로 인한 시스템 자원 소모가 줄어 듭니다.
2. 하나의 스레드가 작업을 할 때 다른 스레드가 별도의 작업을 할 수 있어 사용자와의 응답성도 좋아집니다.

단점
1. 서로 자원을 소모하다가 충돌이 일어날 가능성이 존재합니다.
2. 코딩이 난해해져 버그 생성 확률이 높아집니다.

📖 Thread 실행 제어(스케줄링)

📘 쓰레드 우선순위

쓰레드는 우선순위(priority)라는 속성을 가지고 있고, 이 우선순위에 따라 스케줄러가 할당하는 시간이 달라집니다.
즉 쓰레드가 수행하는 작업의 중요도에 따라 쓰레드의 우선순위를 지정하여 특정 쓰레드가 더 많은 작업시간을 갖도록 처리할 수 있습니다.
ex. 채팅을 처리하는 쓰레드는 파일을 전송하는 쓰레드보다 우선순위가 높아야한다.

우선 순위를 지정하기 위한 상수를 제공

  • static final int MAX_PRIORITY : 우선순위 10 - 가장 높은 우선 순위
  • static final int MIN_PRIORITY : 우선순위 1 - 가장 낮은 우선 순위
  • static final int NORM_PRIORITY : 우선순위 5 - 보통의 우선 순위(main 쓰레드의 초기값)

스레드 우선 순위는 변경 가능

  • void setPriority(int priority)
  • int getPriority()

순환 할당(Round-robin) 방식

  • 시간할당량(Time Slice)을 정해 하나의 스레드를 정해진 시간만큼만 실행하도록 하는 방식.
  • JVM에 의해서 정해지기 때문에 코드로 제어 불가능
  • 동일한 우선 순위의 스레드는 돌아가면서 스케쥴링(라운드 로빈)

ToDo

  • 멀티스레드 병렬처리
  • 스레드 동기화
profile
💻 + ☕ = </>
post-custom-banner

0개의 댓글