Thread (4)

김재익·2023년 6월 19일
0

JAVA

목록 보기
12/18
post-thumbnail

쓰레드의 상태

쓰레드의 흐름도


실행과 대기를 반복하며 run() 메서드를 수행한다.
run() 메서드가 종료되면 실행이 멈추게 된다.

쓰레드의 상태

상태Enum설명
객체생성NEW쓰레드 객체 생성, 아직 start() 메서드 호출 전의 상태
실행대기RUNNABLE실행 상태로 언제든지 갈 수 있는 상태
일시정지WAITING다른 쓰레드가 통지(notify) 할 때까지 기다리는 상태
일시정지TIMED_WAITING주어진 시간 동안 기다리는 상태
일시정지BLOCKED사용하고자 하는 객체의 Lock이 풀릴 때까지 기다리는 상태
종료TERMINATED쓰레드의 작업이 종료된 상태

쓰레드는 일시정지 상태를 만들 수 있다.
일시정지된 쓰레드는 실행을 할 수 없는 상태가 된다.
일시정지된 쓰레드를 다시 실행하려면 일시정지상태에서 실행대기 상태로 넘어가야한다.

쓰레드의 제어

쓰레드 제어 흐름도

sleep(), interrupt()

  • sleep
    • 지정된 시간동안 멈추게한다.
    • 자기자신에 대해서만 멈추게 할 수 있다.
    • 멈춰 있는 동안 interrupt()를 만나면 다시 실행되기 때문에 InterruptException이 발생할 수 있다.
    • InterruptException에 대한 예외처리를 해야한다.
public static void main(String[] args) {
	Runnable task = () -> {
    	try {
        	// (1) 예외처리 필수
       	 	// - interrupt() 를 만나면 다시 실행되기 때문에
       	 	// - InterruptException이 발생할 수 있다.
       	 	// (2) 특정 쓰레드 지목 불가
        	Thread.sleep(2000); // TIMED_WAITTING(주어진 시간동안만 기다리는 상태)

		} catch (InterruptedException e) {
        	e.printStackTrace();
        }
      	System.out.println("task : " + Thread.currentThread().getName());
	};

	Thread thread = new Thread(task, "Thread");
    thread.start(); // NEW -> RUNNABLE

	try {
    	// 1초가 지나고 나면 runnable 상태로 변하여 다시 실행
        // 특정 스레드를 지목해서 멈추게 하는 것은 불가능
        // Static member 'java.lang.Thread.sleep(long)' accessed via instance reference
        // 변수 thread를 지칭해서 sleep을 해도 의미가 없다고 나온다.
        thread.sleep(1000);
        System.out.println("sleep(1000) : " + Thread.currentThread().getName());
	} catch (InterruptedException e) {
    	e.printStackTrace();
	}
}

sleep으로 특정 쓰레드를 지목해서 멈추게 하는 것은 불가능하다.

  • interrupt
    • sleep중인 쓰레드를 실행대기로 전환한다.
    • 예외처리를 통해 InterruptedException을 처리해야한다.
    • 예외처리를 하는 것 보다 쓰레드의 isInterrupted()를 이용하여 예외가 발생하는것을 막을 수 있다. (추천)
public static void main(String[] args) {
	Runnable task = () -> {
    	// 쓰레드 상태 체크 interrupt 되기 전엔 계속 실행 한다.
        while (!Thread.currentThread().isInterrupted()) {
        	try {
        		System.out.println("도달했다.");
            	Thread.sleep(1000);
            	System.out.println(Thread.currentThread().getName());
			} catch (InterruptedException e) {
        		e.printStackTrace();
			}
		}
    		System.out.println("task : " + Thread.currentThread().getName());
	};

	Thread thread = new Thread(task, "Thread");
    thread.start();

	thread.interrupt();

	System.out.println("thread.isInterrupted() = " + thread.isInterrupted());

}

join(), yield()

  • join
    • 정해진 시간동안 지정한 쓰레드가 작업하는 것을 기다린다.
    • 시간을 지정하지 않으면 지정한 쓰레드의 작업이 끝날 때 까지 기다린다.
 public static void main(String[] args) {
 	Runnable task = () -> {
    	try {
        	Thread.sleep(5000); // 5초
		} catch (InterruptedException e) {
        	e.printStackTrace();
        }
    };

	Thread thread = new Thread(task, "thread");

	thread.start();

	long start = System.currentTimeMillis();

	try {
    	// 시간을 지정하지 않았기 때문에 thread가 작업을 끝낼 때까지 main 쓰레드는 대기 하게 된다.
        thread.join();

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

	// thread 의 소요시간인 5000ms 동안 main 쓰레드가 기다렸기 때문에 5000이상이 출력됩니다.
    System.out.println("소요시간 = " + (System.currentTimeMillis() - start));
}
  • yield
    • 남은 시간을 다음 쓰레드에게 양보하고 쓰레드 자신은 실행 대기 상태가 된다.
public static void main(String[] args) {
	Runnable task = () -> {
    	try {
        	for (int i = 0; i < 10; i++) {
            	Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName());
			}
		} catch (InterruptedException e) {
        	// 남은 시간을 다음 쓰레드에게 양보하고 본쓰레드는 실행대기 상태가 된다.
            Thread.yield();
//                e.printStackTrace();
		}
	};

	Thread thread1 = new Thread(task, "thread1");
    Thread thread2 = new Thread(task, "thread2");

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

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

	thread1.interrupt();

}
profile
개발자호소인

0개의 댓글