티스토리 뷰

JAVA/JAVA기본

JAVA - Thread란?

realizers 2022. 1. 27. 16:59
728x90
반응형

Thread란?


  • 스레드란 한 프로세스에서 동작되는 여러 흐름입니다. 쉽게 말하자면 크롬에서 여러개의 탭을 사용중에 있거나 교통체증을 막기위하여 여러개의 차선이 있는것이라고 생각할 수 있습니다.

 

Thread의 상태


  • 경우에 따라서 스레드는 실행 상태에서 실행 대기 상태로 가지 않고 일시 정지 상태로 가기도 하는데, 일시 정지 상태는 스레드가 실행할 수 없는 상태입니다.
  • 스레드가 다시 실행 상태로 가기위해서는 실행 대기 상태로 가야합니다.

스레드 객체 생성 - NEW
  • 스레드 객체 생성 후 아직 start() 메서드가 호출되지 않은 상태입니다.
실행 대기 - RUNNABLE
  • 실행 상태로 언제든지 갈 수 있는 상태입니다.
  • CPU를 점유하고 있지 않으며 실행을 하기 위한 대기 상태입니다. 개발자가 start() 메서드를 호출하면 run() 메서드에 설정된 스레드가 준비상태로 진입합니다.
일시정지
  • WAITING - 다른 스레드가 통지할 때까지 기다리는 상태입니다.
  • TIMED_WAITING - 주어진 시간동안 기다리는 상태입니다.
  • BLOCLED - 사용하고자 하는 객체의 락이 풀릴때까지 기다리는 상태입니다.
종료 - TERMINATED
  • 실행을 마친 상태입니다.

 

Thread의 상태 예제


public class StatePrintThread extends Thread{

    private Thread thread;

    public StatePrintThread(Thread thread) {
        this.thread = thread;
    }

    @Override
    public void run() {

        while (true) {
            Thread.State state = thread.getState();
            System.out.println("스레드 상태: " + state);

            if (state == Thread.State.NEW) {
                thread.start();
            }

            if (state == Thread.State.TERMINATED) {
                break;
            }

            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class TargetThread extends Thread{

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

        try {
            Thread.sleep(1500);
        } catch (Exception e) {
            e.printStackTrace();
        }

        for (int i = 0; i < 1000000000; i++) {}
    }
}

public class Example {
    public static void main(String[] args) {
       StatePrintThread thread = new StatePrintThread(new TargetThread());

       thread.start();
    }
}

 

Thread의 상태 제어


sleep()
  • 주어진 시간동안 일시정지 합니다.
  • 일시 정지인 상태에서 interrupt() 메서드를 호출하면 InterruptedException 예외를 발생시켜 예외처리에서 실행 대기 상태로 가거나 종료상태로 갈 수 있습니다.
public class Example {
    public static void main(String[] args) {
      try {
          Thread.sleep(1000);
          System.out.println("Hello World");
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
    }
}

 

yieid()
  • yieid() 메서드를 호출한 스레드는 실행대기 상태로 돌아가고 동일한 우선순위 또는 높은 우선순위를 갖는 다른 스레드가 실행될 기회를 줍니다.
public class ThreadA extends Thread{
    boolean stop = false;
    boolean work = true;

    @Override
    public void run() {
        while (!stop) {
            if (work) {
                System.out.println("ThreadA가 작업중입니다.");
            } else {
                Thread.yield();
            }
        }

        System.out.println("ThreadA가 종료되었습니다.");
    }
}

public class ThreadB extends Thread{
    boolean stop = false;
    boolean work = true;

    @Override
    public void run() {
        while (!stop) {
            if (work) {
                System.out.println("ThreadB가 작업중입니다.");
            } else {
                Thread.yield();
            }
        }

        System.out.println("ThreadB가 종료되었습니다.");
    }
}

public class Example {
    public static void main(String[] args) {
        ThreadA threadA = new ThreadA();
        ThreadB threadB = new ThreadB();

        threadA.start();
        threadB.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {}
        threadA.work = false; // threadB만 실행

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {}
        threadA.work = true; // threadA, threadB 둘다 실행

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {}
        // 작업 종료
        threadA.stop = true;
        threadB.stop = true;
    }
}

 

join()
  • 다른 스레드가 종료될 때까지 기다렸다가 스레드를 실행할 경우 join() 메서드를 사용합니다.
public class SumThread extends Thread{
    int sum;

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            sum += i;
        }
    }
}

public class Example {
    public static void main(String[] args) {
        SumThread sumThread = new SumThread();
        sumThread.start();

        try {
            sumThread.join();
        } catch (InterruptedException e) {}

        System.out.println("1-100 합: " + sumThread.sum);
    }
}

 

  • join() 메서드를 제거하고 실행하면 sumThread가 작업을 완료하지 않은 상태에서 합을 먼저 출력을 하게 됩니다.
public class Example {
    public static void main(String[] args) {
        SumThread sumThread = new SumThread();
        sumThread.start();

        System.out.println("1-100 합: " + sumThread.sum);
    }
}

 

wait(), notify(), notifyAll()
  • 두개 이상의 스레드를 교대로 번갈아가며 실행해야하는 경우 자신의 작업이 끝나면 상대방 스레드의 일시 정지를 풀어주고, 자신은 일시 정지 상태로 만들어야 합니다. 이 방법의 핵심은 공유 객체에 있습니다.
  • 공유 객체는 두 스레드가 작업할 내용을 각각 동기화 메서드로 구분해 놓고 한 스레드가 작업을 완료하면 notify() 메서드를 호출하여 일시 정지 상태에 있는 다른 스레드를 실행 대기 상태로 만들고 자신은 두번 작업을 하지 않도록 wait() 메서드를 호출하여 일시 정지 상태로 만듭니다.
  • notify() 메서드와 동일한 역할을 하는 notifyAll() 메서드도 존재하는데 notify() 메서드는 wait()에 의해 일시 정지된 스레드 중 하나를 실행 대기 상태로 만들지만 notifyAll() 메서드는 wait() 메서드에 의해 일시 정지된 모든 스레드를 실행 대기 상태로 만듭니다.
  • 해당 메서드들은 동기화 메서드 또는 동기화 블록 내부에서만 사용할 수 있습니다.
public class WorkObject {

    public synchronized void methodA(){
        System.out.println("ThreadA의 methodA() 작업 실행");

        // 일시 정지 상태의 ThreadB를 실행 대기 상태로 만듬
        notify();

        try {
            wait(); // ThreadA를 일시 정지 상태로 만듬
        } catch (InterruptedException e) {}
    }

    public synchronized void methodB(){
        System.out.println("ThreadB의 methodB() 작업 실행");

        // 일시 정지 상태의 ThreadA를 실행 대기 상태로 만듬
        notify();

        try {
            wait(); // ThreadB를 일시 정지 상태로 만듬
        } catch (InterruptedException e) {}
    }
}

public class ThreadA extends Thread{
    private WorkObject workObject;

    ThreadA(WorkObject workObject){
        this.workObject = workObject;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            workObject.methodA();
        }
    }
}

public class ThreadB extends Thread{
    private WorkObject workObject;

    ThreadB(WorkObject workObject){
        this.workObject = workObject;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            workObject.methodB();
        }
    }
}

public class Example {
    public static void main(String[] args) {
        WorkObject workObject = new WorkObject();

        Thread threadA = new ThreadA(workObject);
        Thread threadB = new ThreadB(workObject);

        threadA.start();
        threadB.start();
    }
}

 

Thread의 우선 순위

멀티 스레드는 동시성 또는 병렬성으로 실행이 됩니다.


동시성(Concurrency)
  • 동시성은 멀티 작업을 위해 하나의 코어에서 멀티 스레드가 번갈아가며 실행되는 성질입니다.
병렬성(Parallelism)
  • 병렬성은 멀티 작업을 위해 멀티 코어에서 개별 스레드가 동시에 실행되는 성질을 말합니다.

  • 자바의 스케줄링은 우선 순위 방식과 순환 할당 방식을 이용합니다. 
우선순위 방식
  • 우선순위가 높은 쓰레드가 실행 상태를 더 많이 가지도록 스케줄링하는 것을 말한다. 우선순위 방식은 객체에 우선 순위 번호를 부여할 수 있기 때문에 개발자가 코드로 제어를 할 수 있다.
  • 우선순위는 1~10까지 부여되는데 1이 가장 우선순위가 낮고 10이 가장 높다. 참고로 기본 우선순위는 5이다. 우선순위를 설정하는 방법은 setPriority()메서드를 이용하면 된다.
순환 활당 방식
  • 시간 할당량(Time Slice)을 정해서 하나의 쓰레드를 정해진 시간만큼 실행하고 다시 쓰레드를 실행시키는 방식을 말한다. 이 방식은 JVM에 의해서 정해지기 때문에 개발자가 코드로 제어할 수 없다.
public class CalcThread extends Thread{

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

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

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

public class Example {
    public static void main(String[] args) {
        for (int i = 0; i <= 10; i++) {
            Thread thread = new CalcThread("thread - " + i);

            if (i != 10) {
                thread.setPriority(Thread.MAX_PRIORITY);
            } else {
                thread.setPriority(Thread.MAX_PRIORITY);
            }

            thread.start();
        }
    }
}

 

728x90
반응형