[Kotlin] Kotlin Study #12

이제일·2023년 11월 4일
0

Kotlin

목록 보기
9/10

Thread

  • JVM은 기본적으로 하나의 프로세스가 여러 스레드를 가질 수 있는 Multi Thread 방식 사용
  • 스레드를 사용하기 위해선 Thread 클래스, Runnable 인터페이스 사용
  • 메인 스레드와 서브 스레드로 나눠짐
    • 메인: 프로세스가 기본적으로 가지는 쓰레드로, 오직 한 개만 존재
    • 서브: 주로 I/O 등 오래 걸리는 작업을 할 때 병렬 처리를 위해 생성
  • 주요 함수
    • start : 구성한 스레드 환경 실행(run 실행)
    • run : 다른 스레드 내부에서 실행될 작업
    • jon : 해당 스레드의 종료 시점까지 호출한 스레드를 중지
    • sleep : 스레드 일시 중지

스레드 생성

// Thread
Thread(
	object : Runnable{
		override fun run() {
        	println("Thread Class")
		} 
	}
).start()

// Thread Lambda
Thread { println("Thread Lambda") }.start()

// Thread 상속
class MyThread : Thread() {
    override fun run() {
        println("Thread Extends")
    }
}
MyThread().start()

// 코틀린 제공 함수
thread {
    println("Kotlin Library Thread")
} // default: 생성시 실행

Sync

멀티 스레드 환경에서는 stack을 제외한 자원을 서로 공유하기때문에 하나의 개체에 대해 여러 쓰레드가 접근시 문제가 발생할 수 있다.

var num = 0
for (i in 0 until 1000) {
    thread {
        Thread.sleep(10)
        num += 1
    }
}
Thread.sleep(2000)
print(num) // 1000이 안될 가능성이 높음
  • synchronized 함수

    val lock = Any()
    var num = 0
    for (i in 1..1000) {
        thread {
            Thread.sleep(10)
            synchronized(lock) {
                num += 1
            }
        }
    }
    Thread.sleep(2000)
    print(num) // print: 1000
  • synchronized 어노테이션

    @Synchronized
    fun syncMethod() = run { }
  • AtomicInteger, 원자성 자료형

    val num = AtomicInteger(0)
    for (i in 1..1000) {
        thread {
            Thread.sleep(10)
            num.incrementAndGet()
        }
    }
    Thread.sleep(5000)
    print(num.get()) // print: 1000
  • ReentrantLock, 복잡한 설정

    private val lock: ReentrantLock = ReentrantLock(true)
    fun foo(){
        lock.withLock {
            doSomething()
        }
    }

Thread Pool

하드웨어의 제한적인 사항(CPU, Memory 등)이 있기 때문에
Thread를 관리하는 Thread Pool이라는 개념을 이용한다.

  • FixedThreadPool
    고정된 수의 스레드로 작업 처리하며 추가적인 작업은 작업 큐에서 대기
    작업 흐름이 일정하고 항상 대략 같은 수준일 때 사용

    val executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())
  • CachedThreadPool
    작업의 요구 사항에 따라 동적으로 리소스 활용
    작업 요청이 증가하면 스레드를 생성하고, 유휴 상태로 장시간 대기하면 스레드를 제거

    val executorService = Executors.newCachedThreadPool()
  • SingleThreadExecutor
    하나의 스레드만을 사용, 작업 큐에 있는 작업을 순차적으로 처리
    순차적인 작업이 필요한 경우 사용

    val executorService = Executors.newSingleThreadExecutor()

작업 할당

  • Runnable 작업 완료 후 반환값이 존재하지 않으며 Exception을 발생시키지 않는다.

  • Callable 작업 완료 후 반환값이 존재하며 Exception을 발생
    FutureTask를 통해 Callable 호출 및 Future<V>반환

val callable = Callable<String> { TODO("Not yet implemented") }
val runnable = Runnable { TODO("Not yet implemented") }

작업 처리 요청

  • execute(Runnable)
    작업 처리 결과는 반환하지 않는다.
    처리 도중 예외가 발생하면 Thread가 종료되고 해당 Thread는 Thread Pool에서 제거된다.

  • submit(Callable || Runnable)
    작업 처리 결과를 Future 타입의 객체로 반환한다.
    처리 도중 예외가 발생하면 Thread는 종료되지 않고 다음 작업을 위해 재사용

val future1: Future<String> = executeService.submit(callable)
executeService.execute(runnable)

종료

  • shutdown
    작업 Queue에 남아있는 작업까지 모두 마무리한 후 Thread 종료

  • shutdownNow
    작업 Queue에 남아있는 작업 잔량에 상관없이 강제 종료

  • awaitTermination(long timeout, TimeUnit unit)
    모든 작업 처리를 timeout 시간 안에 처리 시 true 반환, 처리하지 못하면 작업 Thread를 interrupt 시키고 false 반환한다.

profile
세상 제일 이제일

0개의 댓글