[Java] Thread 클래스의 sleep() 은 어떤 Thread를 sleep 시키는가
금주 스터디의 발표를 준비하면서 재밌는 부분을 발견하여서 정리해본다.
Java의 Thread 에는 sleep()이라는 메소드가 있다.
메소드 이름에서도 유추할 수 있듯 입력 값 만큼 스레드를 정지(sleep) 하는 메소드이다.
기존까지에는 거기까지 생각하고 있었다.
Thread 클래스의 sleep() 은 어떤 Thread를 sleep 시키는가
만약 아래와 같은 코드가 있다고 해보자.
Thread th1 = new Thread(() -> {
// th1의 작업 내용
System.out.println("th1 end");
});
Thread th2 = new Thread(() -> {
// th2의 작업 내용
System.out.println("th2 end");
});
th1.start();
th2.start();
try {
th1.sleep(2000);
} catch (InterruptedException e) {
// ...
}
System.out.println("main end");
이 코드를 동작시켰을 때 어떤 출력문이 가장 먼저 출력이 될까?
실행할 때의 상황에 따라 조금씩 차이는 있겠지만 일반적으로 생각하기에는 main end -> th2 end -> th1 end
일 것이라고 생각을 할 것 같다. (적어도 나는 그랬다. th1이 가장 마지막에 호출될 것이라고 기대했다.) 하지만 실제로 실행시켜 보면 th1 end -> th2 end -> main end
순으로 출력이 된다. (main end 가 마지막에 있다는 것이 포인트이다.)
이러한 결과가 나온 이유는 sleep이 static 메소드 이기 때문이다.
sleep은 현재의 thread를 일시정지 해주는 기능을 수행한다. 따라서 th1.sleep(2000)
는 th1 쓰레드가 아닌 main 쓰레드를 일시정지 한다. 따라서 main end
가 가장 마지막에 호출이 된다.
th1.sleep(2000)
은 Thread.sleep(2000)
과 동일하게 동작한다.
sleep 메소드를 호출할 때에는 변수를 통해서 메소드를 호출하는 것이 아닌 클래스를 통해서 호출하는 방식으로 작성해주는것이 좋다. 실제로 intellij 에서는 warning 을 띄워주고 있다.
번외
특정 스레드를 외부에서 sleep 처리하기
그러면 만약 진짜로 th1을 일시정지 해주고 싶었다면 어떻게 처리할 수 있을까? 가능은 할까?
우선 찾아본 결과 이는 자바에서 기본적으로 제공을 해주지는 않는다.
공유 객체와 락을 적절히 사용하면 처리할 수 있다. deadlock에 유의하여야 한다.
sleep 코드 구현 살펴보기
public static void sleep(long millis) throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
long nanos = MILLISECONDS.toNanos(millis);
ThreadSleepEvent event = beforeSleep(nanos);
try {
if (currentThread() instanceof VirtualThread vthread) {
vthread.sleepNanos(nanos);
} else {
sleep0(nanos);
}
} finally {
afterSleep(event);
}
}