HOWTO · Kotlin

Kotlin에서 Sleep으로 스레드 실행 일시 중지하기

Thread.sleep 또는 TimeUnit으로 Kotlin/JVM 스레드를 일시 중지하는 방법과 delay 또는 ScheduledExecutorService를 사용할 시점을 알아봅니다.

이 페이지의 내용

동기 Kotlin/JVM 코드에서 현재 스레드를 일시 중지해야 할 때는 Thread.sleep을 사용합니다. 이름이 있는 시간 단위로 지연을 명확하게 표현하려면 TimeUnit.sleep을 사용합니다. 코루틴에서는 스레드를 차단하지 않고 코루틴을 중지하는 delay를 사용하고, 나중에 실행할 작업에는 ScheduledExecutorService를 사용합니다. 잠자는 모든 스레드는 중단될 수 있으며 지연은 최소 대기 시간이지 정확한 실행 시각이 아닙니다.

coroutine은 비차단 실행 단위를 뜻하는 기술 용어입니다.

Thread.sleep으로 현재 스레드 차단하기

Thread.sleep(milliseconds)는 호출한 스레드를 일시 중지하는 직접적인 JVM 연산입니다. 인수는 밀리초를 나타내는 Long이고 메서드는 InterruptedException을 발생시킵니다. 운영 코드에서는 스택 추적만 출력하지 말고 중단 플래그를 복원하거나 중단을 전달해야 합니다.

fun main() {
    println("before")
    try {
        Thread.sleep(100L)
        println("after")
    } catch (exception: InterruptedException) {
        Thread.currentThread().interrupt()
        println("interrupted")
    }
}

출력:

before
after

Thread에서 Kotlin Sleep 함수 사용하기

호출 스레드는 최소 약 100밀리초 동안 차단됩니다. JVM 전체가 멈추는 것은 아니므로 실행 가능한 다른 스레드는 계속 진행할 수 있습니다. 작업을 비차단 방식의 일시 중지로 표현할 수 있다면 이벤트 루프나 제한된 작업 스레드에서 이 방법을 피하세요.

TimeUnit.sleep으로 읽기 쉬운 단위 사용하기

java.util.concurrent의 TimeUnit은 sleep 전에 초나 밀리초 같은 이름 있는 단위를 변환합니다. 현재 스레드를 계속 차단하고 중단에도 응답하지만 TimeUnit.SECONDS.sleep(1)은 밀리초를 직접 변환하는 코드보다 검토하기 쉽습니다.

import java.util.concurrent.TimeUnit

fun main() {
    println("before")
    try {
        TimeUnit.MILLISECONDS.sleep(100)
        println("after")
    } catch (exception: InterruptedException) {
        Thread.currentThread().interrupt()
        println("interrupted")
    }
}

출력:

before
after

TimeUnit에서 Kotlin Sleep 함수 사용하기

요구 사항에 맞는 단위를 선택하세요. 스케줄링, 경합 또는 운영 체제 타이머의 정밀도 때문에 스레드가 더 늦게 재개될 수 있으므로 변환이 정확한 타이밍을 보장하지는 않습니다.

코루틴에서 delay 사용하기

delay는 kotlinx-coroutines-core의 일시 중단 함수입니다. 기본 스레드를 차단하지 않고 코루틴을 중지하므로 다른 코루틴이 해당 스레드를 사용할 수 있습니다. 일시 중단 함수나 코루틴 스코프에서 호출해야 하며 일반 동기 코드에서 Thread.sleep을 대체하지는 않습니다.

Kotlin delay 함수 사용하기

예를 들어 runBlocking 스코프에서 delay(100L)을 호출한 뒤 메시지를 출력하는 코루틴을 시작할 수 있습니다. 메시지 순서는 스케줄링에 따라 달라집니다. delay는 취소 가능하므로 취소된 코루틴은 일반적으로 일시 중지 지점에서 멈춥니다. 코루틴 코드를 컴파일하기 전에 kotlinx-coroutines-core 의존성을 추가하세요. Kotlin 표준 라이브러리만으로는 delay를 제공하지 않습니다.

ScheduledExecutorService로 작업 예약하기

요구 사항이 “현재 스레드를 재우기”가 아니라 “이 작업을 나중에 실행하기”라면 ScheduledExecutorService를 사용하세요. 예약된 작업은 실행 전에 취소할 수 있으며 더 이상 필요하지 않은 Executor는 종료해야 합니다.

import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

fun main() {
    val executor = Executors.newSingleThreadScheduledExecutor()
    val task = executor.schedule(
        { println("task ran") },
        100,
        TimeUnit.MILLISECONDS,
    )

    task.get()
    executor.shutdown()
}

출력:

task ran

여기서 get()은 작업이 끝날 때까지 짧은 명령줄 예제를 유지하기 위한 것입니다. 애플리케이션에서 취소가 필요하면 반환된 ScheduledFuture를 보관하고 수명 주기 정책에 따라 shutdown 또는 shutdownNow를 사용하세요. 예약은 정확한 실시간 실행을 보장하지 않습니다.

어떤 Kotlin Sleep 방식을 선택해야 할까요?

  • 현재 스레드를 의도적으로 차단하는 작은 동기 JVM 작업에는 Thread.sleep을 사용합니다.
  • 읽기 쉬운 단위로 같은 차단 동작을 표현하려면 TimeUnit.sleep을 사용합니다.
  • 코루틴 안에서 취소 가능하고 비차단인 일시 중지에는 delay를 사용합니다.
  • 지연 후 별도의 작업을 시작해야 할 때는 ScheduledExecutorService를 사용합니다.

일반적인 Sleep 작업으로 wait()을 사용하지 마세요. Java의 Object.wait()은 모니터 조정에 참여하고 적절한 모니터가 필요하므로 다른 동기화 문제를 해결하는 기능입니다. 어떤 API를 선택하든 중단과 취소를 예상하지 못한 오류가 아니라 정상적인 제어 흐름으로 처리하세요.