멀티스레드 환경에서 여러 스레드가 자원에 접근할 때, 올바르게 동작하도록 보장한다는 것을 의미.
Mutual Exclusion, Atomicity, Visibility, Sequential Consistency를 만족해야 한다.
코루틴은 작업을 비동기적으로 처리하며 스레드는 차단되지 않고 다른 작업을 수행하는 Non-Blocking + Asynchronous이다.
간단한 예제와 함께 코루틴을 이해해보자
fun main() = runBlocking {
launch { doWorld() }
println("Hello")
}
suspend fun doWorld() {
delay(1000L)
println("World!")
}
runBlocking{}은 코루틴 스코프를 제공한다. 해당 코드 블록이 완료될 때까지 현재 스레드를 차단한다.launch는 새로운 코루틴을 생성하고 비동기적으로 실행된다. launch { doWorld() }와 println(”Hello”)가 동시에 실행된다. suspend는 함수를 일시 중단 가능 상태로 만든다. 일시 중단된 동안에도 스레드는 차단되지 않고 다른 작업을 수행할 수 있다. 반드시 코루틴내에서만 선언되어야 한다.Scope Builder는 runBlocking뿐만 아니라 coroutineScope도 있다.
fun main() = runBlocking {
doWorld()
}
suspend fun doWorld() = coroutineScope { // this: CoroutineScope
launch {
delay(1000L)
println("World!")
}
println("Hello")
}
둘 다 해당 블록의 본문과 자식 코루틴들이 완료될 때까지 대기한다는 공통점이 있지만 어느 부분이 다를까?
suspend가 일시 중단된다는 개념을 보다 잘 이해하기 위해선 코루틴과 스레드의 차이를 알아야 한다. 확실히 짚고 넘어가자.
쉽게 말해 Coroutine은 Thread를 더 잘게 쪼개 사용하는 방식이다.
Thread는 개발자가 아닌 OS가 관리하며 자신만의 스택과 레지스터가 있고, 메모리 공간(Heap)을 공유한다.
Context Switching이 발생할 때, 자원을 교환하므로 비용이 상대적으로 크다.
Coroutine은 사용자 수준의 Thread로 Context Switching이 훨씬 가볍고 비용이 적게든다.
수천 개의 Coroutine 을 단일 Thread에서 실행할 수 있다. Thread를 차단하지 않고 일시 중단하여 나중에 재개한다.

출처: https://aaronryu.github.io/2019/05/27/coroutine-and-thread/
Coroutine 이 가볍다는 의미는 무엇일까?
작업의 단위는 Object이므로 Task1이 수행중에 비동기 Task2가 발생하더라도 Task1을 수행하던 같은 Thread에서 Task2를 수행할 수 있으며 Heap영역에서 발생하므로 데이터를 주고받지 않아도 된다. 단일 Thread에서 Coroutine Object만 교체하면 되므로 OS레벨의 Context Switching이 발생하지 않기에 보다 가볍다는 얘기이다.