본문 바로가기

1.프로그래밍/Java

[Java] Thread 란? (Thread 총 정리)

728x90
반응형

[Java] Thread 란? (Thread 총 정리)

Process 와 Thread

먼저 프로세스(Process)란 cpu에 의해 메모리에 올라가 실행중인 프로그램을 말한다.


자신만의 메모리 공간을 포함한 독립적인 실행공간을 갖고있다.


자바 JVM(Java Virtual Machine)은 주로 하나의 프로세스로 실행되며, 동시에 여러 작업을 수행하기 위헤 멀티 스레드를 지원한다.


Thread프로세스 안에서 실질적으로 작업을 실행하는 단위를 말한다.


Java에서는 JVM에 의해 관리가 된다.


한 프로그램에 여러개의 스레드가 존재 가능하며, 스레드가 1개이면 단일 스레드, 2개 이상이면 멀티 스레드 환경이 된다.

Thread State

https://static.javatpoint.com/core/images/life-cycle-of-a-thread.png


  • Thread.State NEW : 스레드가 실행 준비가 완료된 상태, 스레드의 첫 시작.
  • Thread.State RUNNABLE : 스레드가 실행 가능한 상태, 스레드가 대기열에서 실행을 기다리고 있음을 의미.
  • Thread.State BLOCKED : 스레드가 차단되어 있는 상태, 스레드가 잠금(lock) 습득을 기다리는 상태.
  • Thread.State WAITING : 스레드가 대기중인 상태, 대기 상태의 스레드는 다른 스레드가 작업을 완료하기를 기다리고 있는 상태이다.
  • Thread.State TIMED_WAITING : 스레드가 정해진 시간동안 대기하는 상태, WAITING과의 차이는 정해진 시간동안 대기를 한다는 것이다.
  • Thread.State TERMINATED : 스레드가 종료되거나 죽은 상태, 종료된 스레드는 실행이 완료되었음을 의미한다.

Java Thread class 와 Runnable interface

Java에서 Thread를 생성하는 방법은 크게 두가지 이다.
하나는 Thread 클래스를 상속 받아 생성하는 방법이다. run() 메서드를 오버라이딩
다른 하나는 Runnable 인터페이스를 구현하여 생성하는 방법이 있다. run() 메서드 구현


보통 Runnable인터페이스를 구현하는 방법으로 많이 생성하여 사용한다.
그 이유는 매우 간단하게, Thread클래스를 extends해버리게 되면, 다른 클래스를 더 이상 상속할 수 없기 때문이다.


단, Runnable을 구현하여 스레드를 생성하는 경우, 객체 참조변수를 인자값으로 하는 Thread를 생성하여 사용해야 된다.

반면, java.lang.Thread 클래스를 상속받아 사용하는 경우 실행 스레드로 자신의 콜 스택을 가진 독립적인 프로세스가 된다.


Thread.Class Method

Method Content Exception
void sleep(long msec) 지정된 millisecond 만큼 대기 throws Interrupted Exception
String getName() 스레드 이름 반환
void setName(String str) 스레드 이름 str 지정
void start() 스레드 시작, run() 메서드 호출
int getPriority() 스레드 우선순위 반환
void setPriority(int p) 스레드 우선순위 p로 설정
boolean isAlive() 스레드가 시작되어 아직 끝나지 않았으면 return true, 끝났으면 return false
void join() 스레드가 끝날 때 까지 대기 throws Interupted Exception
void run() 스레드가 실행하는 함수 (오버라이딩 재정의)
void suspend() 스레드를 일시정지 시킨다.
void resume() 일시정지된 스레드를 다시 시작
void yield() 다른 스레드에게 실행 상태 양보 후, 준비 상태로 변환
void notify() Wait 상태에 있는 스레드를 Runnable 상태로 만듬
Thread currentThread() 현재 실행중인 스레드를 반환
Thread.State getState() 스레드의 상태 반환
void interrupt() 스레드를 중단
boolean isInterrupt() 스레드가 중단 되었는지 boolean 반환

Java Thread 사용하기

Extends Thread.class

package thread;

import java.lang.Thread;

class MyThread extends Thread{

    int loopCount;

    public MyThread() {
        this.loopCount = 0;
    }

    @Override
    public void run() {

        for(int i = 0; i < 5; i++){
            System.out.println("Thread getName() : " + Thread.currentThread().getName() + "  loopCount : " + loopCount);
            loopCount++;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

public class Test {
    public static void main(String[] args) {
        MyThread thread =  new MyThread();

        thread.start();
    }
}

// 출력
/*
Thread getName() : Thread-0  loopCount : 0
Thread getName() : Thread-0  loopCount : 1
Thread getName() : Thread-0  loopCount : 2
Thread getName() : Thread-0  loopCount : 3
Thread getName() : Thread-0  loopCount : 4
*/

Implement Runnable.interface

package thread;

import java.lang.Thread;

class MyThread implements Runnable{

    int loopCount;

    public MyThread() {
        this.loopCount = 0;
    }

    @Override
    public void run() {

        for(int i = 0; i < 5; i++){
            System.out.println("Thread getName() : " + Thread.currentThread().getName() + "  loopCount : " + loopCount);
            loopCount++;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

public class Test {
    public static void main(String[] args) {
        MyThread obj =  new MyThread();
        Thread thread = new Thread(obj);

        thread.start();
    }
}


// 출력
/*
Thread getName() : Thread-0  loopCount : 0
Thread getName() : Thread-0  loopCount : 1
Thread getName() : Thread-0  loopCount : 2
Thread getName() : Thread-0  loopCount : 3
Thread getName() : Thread-0  loopCount : 4
*/

위에 코드처럼, Thread class extends 하느냐, 혹은 Runnable interface 를 implement 하느냐, 두가지의 차이로 Thread를 생성하여 사용할 수 있다.


Thread.sleep() 메서드를 이용하여 1초 단위로 출력되게 만들 수 있다.


두 개의 Thread

package thread;

import java.lang.Thread;

class MyThread implements Runnable{

    int loopCount;
    int millisecond;

    public MyThread(int millisecond) {
        this.loopCount = 0;
        this.millisecond = millisecond;
    }

    @Override
    public void run() {

        for(int i = 0; i < 5; i++){
            System.out.println("Thread getName() : " + Thread.currentThread().getName() + "  loopCount : " + loopCount);
            loopCount++;
            try {
                Thread.sleep(this.millisecond);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

public class Test {
    public static void main(String[] args) {
        MyThread obj1 =  new MyThread(3000);
        MyThread obj2 = new MyThread(5000);
        Thread thread1 = new Thread(obj1);
        Thread thread2 = new Thread(obj2);

        thread1.start();
        thread2.start();
    }
}

// 출력
/*
Thread getName() : Thread-0  loopCount : 0
Thread getName() : Thread-1  loopCount : 0
Thread getName() : Thread-0  loopCount : 1
Thread getName() : Thread-1  loopCount : 1
Thread getName() : Thread-0  loopCount : 2
Thread getName() : Thread-0  loopCount : 3
Thread getName() : Thread-1  loopCount : 2
Thread getName() : Thread-0  loopCount : 4
Thread getName() : Thread-1  loopCount : 3
Thread getName() : Thread-1  loopCount : 4
*/

두개의 스레드를 생성하여 만들어 본 예제 코드이다.
여기서 볼 점은 Thread.sleep()의 millisecond를 다르게 주었다는 것이다.
즉, 대기시간이 서로 상이하기 때문에, 직접 실행하게 되면 조금씩 두번째 스레드가 밀리게 된다.
출력값을 볼 때 스레드의 이름과 loopCount를 매칭해서 보면 이해가 된다.


참고로, Thread.sleep() 메서드에 음수의 값을넣게 되면 java.lang.IllegalArgumentException: timeout value is negative 라는 Exception이 나오게 된다.


Thread join()

package thread;

import java.lang.Thread;

class MyThread implements Runnable{

    int loopCount;
    int millisecond;

    public MyThread(int millisecond) {
        this.loopCount = 0;
        this.millisecond = millisecond;
    }

    @Override
    public void run() {

        for(int i = 0; i < 5; i++){
            System.out.println("Thread getName() : " + Thread.currentThread().getName() + "  loopCount : " + loopCount);
            loopCount++;
            try {
                Thread.sleep(this.millisecond);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

public class Test {
    public static void main(String[] args) {
        MyThread obj1 =  new MyThread(3000);
        MyThread obj2 = new MyThread(5000);
        Thread thread1 = new Thread(obj1);
        Thread thread2 = new Thread(obj2);

        thread1.start();

        try {
            thread1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread2.start();
    }
}

// 출력
/*
Thread getName() : Thread-0  loopCount : 0
Thread getName() : Thread-0  loopCount : 1
Thread getName() : Thread-0  loopCount : 2
Thread getName() : Thread-0  loopCount : 3
Thread getName() : Thread-0  loopCount : 4
Thread getName() : Thread-1  loopCount : 0
Thread getName() : Thread-1  loopCount : 1
Thread getName() : Thread-1  loopCount : 2
Thread getName() : Thread-1  loopCount : 3
Thread getName() : Thread-1  loopCount : 4
*/

이번에는 Thread.join() 메서드를 사용한 것이다.
join() 메서드는 스레드가 끝날 때 까지 대기시키는 메서드이다.


즉, 메인 메서드에서, thread1.join()을 통해 thread1이 끝날 때 까지 그 뒤의 스레드는 실행되지 않고,
대기상태에 놓이게 되고, thread1이 실행이 끝나게 되면 thread2가 실행되게 된다.


Thread priority

package thread;

import java.lang.Thread;

class MyThread implements Runnable{

    int loopCount;
    int millisecond;

    public MyThread(int millisecond) {
        this.loopCount = 0;
        this.millisecond = millisecond;
    }

    @Override
    public void run() {

        for(int i = 0; i < 5; i++){
            System.out.println("Thread getName() : " + Thread.currentThread().getName() + "  loopCount : " + loopCount);
            loopCount++;
            try {
                Thread.sleep(this.millisecond);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

public class Test {
    public static void main(String[] args) {
        MyThread obj1 =  new MyThread(1000);
        MyThread obj2 = new MyThread(1000);
        Thread thread1 = new Thread(obj1);
        Thread thread2 = new Thread(obj2);

        thread1.setPriority(3);
        thread2.setPriority(10);

        thread1.start();


        thread2.start();
    }
}

// 출력
/*
Thread getName() : Thread-1  loopCount : 0
Thread getName() : Thread-0  loopCount : 0
Thread getName() : Thread-1  loopCount : 1
Thread getName() : Thread-0  loopCount : 1
Thread getName() : Thread-1  loopCount : 2
Thread getName() : Thread-0  loopCount : 2
Thread getName() : Thread-1  loopCount : 3
Thread getName() : Thread-0  loopCount : 3
Thread getName() : Thread-1  loopCount : 4
Thread getName() : Thread-0  loopCount : 4
*/

이번에는 setPriority() 메서드를 통해 우선순위를 설정해 줬다.
그런후 sleep()의 타임을 동일하게 설정해 줬다.


이렇게 할 경우, 우선순위가 더 높은 스레드가 먼저 실행이 되는 결과를 볼 수 있다.


Thread Interrupt

package thread;

import java.lang.Thread;

class MyThread implements Runnable{

    int loopCount;
    int millisecond;
    Thread thread;

    public MyThread(int millisecond) {
        this.loopCount = 0;
        this.millisecond = millisecond;
        this.thread = new Thread();
    }

    public void start(){
        this.thread = new Thread(this);
        this.thread.run();
    }

    @Override
    public void run() {

        System.out.println(this.thread.getState());
        while(!this.thread.isInterrupted()){
            try{
                if(this.loopCount == 5){
                    this.thread.interrupt();
                    System.out.println("Thread interrupt!");
                    return;
                }

                Thread.sleep(this.millisecond);

                System.out.println("Thread getName() : " + Thread.currentThread().getName() + " loopCount : " + this.loopCount);
                System.out.println(Thread.currentThread().getState());
                this.loopCount++;

            } catch (InterruptedException e){
                e.printStackTrace();
            }
        }

    }
}

public class Test {
    public static void main(String[] args) {
        MyThread obj1 =  new MyThread(1000);

        obj1.start();

    }
}

// 출력
/*
NEW
Thread getName() : main loopCount : 0
RUNNABLE
Thread getName() : main loopCount : 1
RUNNABLE
Thread getName() : main loopCount : 2
RUNNABLE
Thread getName() : main loopCount : 3
RUNNABLE
Thread getName() : main loopCount : 4
RUNNABLE
Thread interrupt!
*/

이번에는 코드를 좀 변경시켜 봤다.
MyThread 라는 클래스내에 Thread 객체를 생성시켜, 실행시켜보았다.


loopCount가 5가 되면 interrupt()메서드를 사용하여 스레드를 강제 종료시키게 하였다.

위의 방식처럼 스레드를 만들어서 자유자재로 사용하게 된다면, 객체 내에 다양한 로직들을 추가하여
스레드를 사용할 수 있게 될 것 같다.

728x90
반응형