diff --git a/docs/images/coroutines-and-threads.svg b/docs/images/coroutines-and-threads.svg new file mode 100644 index 0000000000..ed38d9d52b --- /dev/null +++ b/docs/images/coroutines-and-threads.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/parallelism-and-concurrency.svg b/docs/images/parallelism-and-concurrency.svg new file mode 100644 index 0000000000..6e56f96ede --- /dev/null +++ b/docs/images/parallelism-and-concurrency.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/images/run-icon.png b/docs/images/run-icon.png new file mode 100644 index 0000000000..ebc6d3e91c Binary files /dev/null and b/docs/images/run-icon.png differ diff --git a/docs/topics/coroutines-basics.md b/docs/topics/coroutines-basics.md index 5b467c58bf..8ba32f8237 100644 --- a/docs/topics/coroutines-basics.md +++ b/docs/topics/coroutines-basics.md @@ -1,293 +1,566 @@ - https://github.com/Kotlin/kotlinx.coroutines/edit/master/docs/topics/ [//]: # (title: Coroutines basics) -This section covers basic coroutine concepts. +To create applications that perform multiple tasks at once, a concept known as concurrency, +Kotlin uses coroutines. Coroutines let you write concurrent code in a clear and sequential style. -## Your first coroutine +The most basic building block of coroutines is the suspending function. +It makes it possible to pause a running operation and resume it later without losing the structure of your code. -A _coroutine_ is an instance of a suspendable computation. It is conceptually similar to a thread, in the sense that it -takes a block of code to run that works concurrently with the rest of the code. -However, a coroutine is not bound to any particular thread. It may suspend its execution in one thread and resume in another one. +To mark a function as a suspending function, use the `suspend` keyword: -Coroutines can be thought of as light-weight threads, but there is a number -of important differences that make their real-life usage very different from threads. +```kotlin +suspend fun greet() { + println("Hello world from a suspending function") +} +``` -Run the following code to get to your first working coroutine: +You can only call a suspending function from another suspending function. +To call suspending functions at the entry point of a Kotlin application, mark the `main()` function with the `suspend` keyword: ```kotlin -import kotlinx.coroutines.* +suspend fun main() { + showUserInfo() +} -//sampleStart -fun main() = runBlocking { // this: CoroutineScope - launch { // launch a new coroutine and continue - delay(1000L) // non-blocking delay for 1 second (default time unit is ms) - println("World!") // print after delay - } - println("Hello") // main coroutine continues while a previous one is delayed +suspend fun showUserInfo() { + println("Loading user...") + greet() + println("User: John Smith") +} + +suspend fun greet() { + println("Hello world from a suspending function") } -//sampleEnd ``` -{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} - -> You can get the full code [here](https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-basic-01.kt). -> -{style="note"} +{kotlin-runnable="true"} -You will see the following result: +This example doesn't use concurrency yet, but by marking the functions with the `suspend` keyword, +you allow them to call other suspending functions and run concurrent code inside. -```text -Hello -World! -``` +While the `suspend` keyword is part of the core Kotlin language, most coroutine features +are available through the [`kotlinx.coroutines`](https://github.com/Kotlin/kotlinx.coroutines) library. + +## Add the kotlinx.coroutines library to your project - +To include the `kotlinx.coroutines` library in your project, add the corresponding dependency configuration based on your build tool. -Let's dissect what this code does. +### Gradle -[launch] is a _coroutine builder_. It launches a new coroutine concurrently with -the rest of the code, which continues to work independently. That's why `Hello` has been printed first. +Add the following dependency to your `build.gradle(.kts)` file: + + + + +```kotlin +// build.gradle.kts +repositories { + mavenCentral() +} -[delay] is a special _suspending function_. It _suspends_ the coroutine for a specific time. Suspending a coroutine -does not _block_ the underlying thread, but allows other coroutines to run and use the underlying thread for -their code. +dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:%coroutinesVersion%") +} +``` -[runBlocking] is also a coroutine builder that bridges the non-coroutine world of a regular `fun main()` and -the code with coroutines inside of `runBlocking { ... }` curly braces. This is highlighted in an IDE by -`this: CoroutineScope` hint right after the `runBlocking` opening curly brace. + + -If you remove or forget `runBlocking` in this code, you'll get an error on the [launch] call, since `launch` -is declared only on the [CoroutineScope]: +```groovy +// build.gradle +repositories { + mavenCentral() +} +dependencies { + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:%coroutinesVersion%' +} ``` -Unresolved reference: launch + + + +### Maven + +Add the following dependency to your `pom.xml` file. + +```xml + + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + %coroutinesVersion% + + + ... + ``` -The name of `runBlocking` means that the thread that runs it (in this case — the main thread) gets _blocked_ for -the duration of the call, until all the coroutines inside `runBlocking { ... }` complete their execution. You will -often see `runBlocking` used like that at the very top-level of the application and quite rarely inside the real code, -as threads are expensive resources and blocking them is inefficient and is often not desired. +## Create your first coroutines + +A coroutine is a suspendable computation that can run concurrently with other coroutines and potentially in parallel. + +On the JVM and in Kotlin/Native, all concurrent code, such as coroutines, runs on _threads_, which are managed by the operating system. +Coroutines can suspend their execution instead of blocking a thread. +This allows one coroutine to suspend while waiting for some data to arrive and another coroutine to run on the same thread, ensuring effective resource utilization. + +![Comparing parallel and concurrent threads](parallelism-and-concurrency.svg){width="700"} + +To create a coroutine in Kotlin, you need the following: + +* A suspending function. +* A coroutine scope in which it can run, such as one available inside the `withContext()` function. +* A coroutine builder like `.launch()` to start it. +* A dispatcher to control which threads it uses. + +> You can display coroutine names next to thread names in the output of your code for additional information. +> To do so, pass the `-Dkotlinx.coroutines.debug` VM option in your build tool or IDE run configuration. +> +> See [Debugging coroutines](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/topics/debugging.md) for more information. +> +{style="tip"} + +Let's look at an example that uses multiple coroutines in a multithreaded environment: + +1. Import the `kotlinx.coroutines` library: + + ```kotlin + import kotlinx.coroutines.* + ``` + +2. Mark functions that can pause and resume with the `suspend` keyword: + + ```kotlin + suspend fun greet() { + println("Hello world from a suspending function on thread: ${Thread.currentThread().name}") + } + + suspend fun main() {} + ``` + + > While you can mark the `main()` function as `suspend` in some projects, it may not be possible when integrating with existing code or using a framework. + > In that case, check the framework's documentation to see if it supports calling suspending functions. + > If not, use [`runBlocking`](#runblocking) to call them by blocking the current thread. + > + {style="note"} + + +3. Use [`withContext(Dispatchers.Default)`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html#) to define an entry point for multithreaded concurrent code that runs on a shared thread pool: + + ```kotlin + suspend fun main() { + withContext(Dispatchers.Default) { + // Add the coroutine builders here + } + } + ``` + + > The suspending `withContext()` function is typically used for [context switching](coroutine-context-and-dispatchers.md#jumping-between-threads), but in this example, + > it also defines a non-blocking entry point for concurrent code. + > It uses the [`Dispatchers.Default` dispatcher](#coroutine-dispatchers) to run code on a shared thread pool for multithreaded execution. + > + > The coroutines launched inside the `withContext()` block share the same coroutine scope, which ensures [structured concurrency](#coroutine-scope-and-structured-concurrency). + > + {style="note"} + +4. Use a [coroutine builder function](#coroutine-builder-functions) like [`.launch()`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html) to start the coroutine: + + ```kotlin + suspend fun main() { + withContext(Dispatchers.Default) { + // Starts a coroutine inside the scope + launch { greet() } + println("This runs concurrently and possibly in parallel with the launched coroutines on thread: ${Thread.currentThread().name}") + } + } + ``` + +5. Combine these pieces to run multiple coroutines at the same time on a shared pool of threads: + + ```kotlin + // Imports the coroutines library + import kotlinx.coroutines.* + + // Defines a suspending function + suspend fun greet() { + println("Hello from greet() on thread: ${Thread.currentThread().name}") + } + + + suspend fun main() { + // Runs all concurrent code on a shared thread pool + withContext(Dispatchers.Default) { + launch() { + greet() + // You can add suspending API calls here, such as a network request + } + + // Starts another coroutine + launch() { + println("Another coroutine on thread: ${Thread.currentThread().name}") + // You can add suspending API calls here, such as a network request + } + + println("This runs concurrently and possibly in parallel with the launched coroutines on thread: ${Thread.currentThread().name}") + } + } + ``` + {kotlin-runnable="true"} + +This example demonstrates simple multithreading with coroutines on a shared thread pool. + +> Try running the example multiple times. +> You may notice that the output order and thread names may change each time you run the program. +> This is because the OS decides when threads run and tasks complete. +> +{style="tip"} + +## Coroutine scope and structured concurrency -### Structured concurrency +When you run many coroutines in an application, you need a way to manage them as groups. +Kotlin coroutines rely on a principle called _structured concurrency_ to provide this structure. -Coroutines follow a principle of -**structured concurrency** which means that new coroutines can only be launched in a specific [CoroutineScope] -which delimits the lifetime of the coroutine. The above example shows that [runBlocking] establishes the corresponding -scope and that is why the previous example waits until `World!` is printed after a second's delay and only then exits. +This principle groups coroutines into a hierarchy of parent and child tasks with linked lifecycles. +Coroutines form a tree hierarchy where each coroutine has at most one parent and can have any number of children. +A parent coroutine waits for its children to complete before it completes. +If the parent coroutine fails or is canceled, all its child coroutines are canceled too. +Keeping coroutines connected this way makes cancellation, error handling, and resource cleanup predictable and safe. -In a real application, you will be launching a lot of coroutines. Structured concurrency ensures that they are not -lost and do not leak. An outer scope cannot complete until all its children coroutines complete. -Structured concurrency also ensures that any errors in the code are properly reported and are never lost. +> A coroutine's lifecycle is the period from its creation until it completes, fails, or is canceled. +> +{style="tip"} + +To maintain structured concurrency, new coroutines can only be launched in a [`CoroutineScope`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/) that defines and manages their lifecycle. +When you start a coroutine inside another coroutine, it automatically becomes a child of its parent scope. +The parent coroutine's scope waits for all its children to finish before it completes. + +Calling a [coroutine builder function](#coroutine-builder-functions) such as `.launch()` on a `CoroutineScope` starts a child coroutine of the coroutine associated with that scope. +Inside the builder's block, the [receiver](lambdas.md#function-literals-with-receiver) is a `CoroutineScope`, so any coroutines you launch there become its children. +I don't want to be too explicit here. I want to explain this further down in the next section. -## Extract function refactoring +While you can use the `withContext()` function to create a scope for your coroutines, +you can use the [`coroutineScope()`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html) function when you want to create a new coroutine scope without changing the context. +It executes the suspending block inline and suspends until the block and any coroutines launched in it complete. -Let's extract the block of code inside `launch { ... }` into a separate function. When you -perform "Extract function" refactoring on this code, you get a new function with the `suspend` modifier. -This is your first _suspending function_. Suspending functions can be used inside coroutines -just like regular functions, but their additional feature is that they can, in turn, -use other suspending functions (like `delay` in this example) to _suspend_ execution of a coroutine. +Here's an example: ```kotlin +// Imports the kotlin.time.Duration to express duration in seconds +import kotlin.time.Duration.Companion.seconds + import kotlinx.coroutines.* -//sampleStart -fun main() = runBlocking { // this: CoroutineScope - launch { doWorld() } - println("Hello") -} +suspend fun main() { + println("Starting coroutine scope") + coroutineScope { + launch { + delay(1.seconds) + println("The first coroutine completed") + } + launch { + delay(2.seconds) + println("The second coroutine completed") + } + } -// this is your first suspending function -suspend fun doWorld() { - delay(1000L) - println("World!") + // Runs only after all children in the coroutineScope have completed + println("Coroutine scope completed") } -//sampleEnd ``` -{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} - -> You can get the full code [here](https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-basic-02.kt). -> -{style="note"} +{kotlin-runnable="true"} - +This example uses the [`delay()`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html#) function to show how the coroutine scope waits for its child coroutines to finish. +You can specify the wait time in milliseconds, so `delay(1000L)` suspends the coroutine for one second without blocking the thread. -## Scope builder +> Use [`kotlin.time.Duration`](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.time/-duration/) from the Kotlin standard library to express durations like `delay(1.seconds)` instead of using milliseconds. +> +{style="tip"} -In addition to the coroutine scope provided by different builders, it is possible to declare your own scope using the -[coroutineScope][_coroutineScope] builder. It creates a coroutine scope and does not complete until all launched children complete. +### Extract coroutine builders from the coroutine scope -[runBlocking] and [coroutineScope][_coroutineScope] builders may look similar because they both wait for their body and all its children to complete. -The main difference is that the [runBlocking] method _blocks_ the current thread for waiting, -while [coroutineScope][_coroutineScope] just suspends, releasing the underlying thread for other usages. -Because of that difference, [runBlocking] is a regular function and [coroutineScope][_coroutineScope] is a suspending function. +> For more information on how lambdas with receivers work in Kotlin, see [Function literals with receiver](lambdas.md#function-literals-with-receiver). +> +{style="tip"} -You can use `coroutineScope` from any suspending function. -For example, you can move the concurrent printing of `Hello` and `World` into a `suspend fun doWorld()` function: +The `coroutineScope()` function takes a lambda with a `CoroutineScope` receiver. +Inside this lambda, the implicit receiver is a `CoroutineScope`, so builder functions like `.launch()` and `.async()` resolve as +[extension functions](extensions.md#extension-functions) on that receiver: ```kotlin -import kotlinx.coroutines.* +suspend fun main() = coroutineScope { // this: CoroutineScope + // Calls CoroutineScope.launch() where CoroutineScope is the receiver + this.launch { println("1") } + this.launch { println("2") } +} +``` + +To extract the coroutine builders into another function, that function must declare a `CoroutineScope` receiver: -//sampleStart -fun main() = runBlocking { - doWorld() +```kotlin +suspend fun main() { + coroutineScope { // this: CoroutineScope #1 + // launchAll() creates its own child CoroutineScope + launchAll() + } +} + +// Doesn't declare CoroutineScope as the receiver +suspend fun launchAll() { + // Error: unresolved reference launch() + this.launch { println("1") } + this.launch { println("2") } +} + +// Creates a new CoroutineScope as the receiver for the .launch() coroutine builders +suspend fun launchAll() = coroutineScope { // this: CoroutineScope #2 + // Calls .launch() on CoroutineScope #2 + this.launch { println("1") } + this.launch { println("2") } } +``` +{kotlin-runnable="true"} + +## Coroutine builder functions + +A coroutine builder function is a function that accepts a `suspend` [lambda](lambdas.md) that defines a coroutine to run. +This includes the following: + +* [`.launch()`](#coroutinescope-launch) +* [`.async()`](#coroutinescope-async) +* [`runBlocking()`](#runblocking) +* `withContext()` +* `coroutineScope()` + +Coroutine builder functions create new coroutines inside an existing [coroutine scope](#coroutine-scope-and-structured-concurrency). +They require a `CoroutineScope` to run in, either one that is already available, +or one you create using helper functions such as `coroutineScope()`, [`runBlocking()`](#runblocking), or [`withContext()`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html#). +Each builder defines how the coroutine starts and how you interact with its result. + +### CoroutineScope.launch() + +The [`CoroutineScope.launch()`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html#) coroutine builder function is an extension function on `CoroutineScope`. +It starts a new coroutine without blocking the rest of the scope. +Use `.launch()` to run a task alongside other work when the result isn't needed or you don't want to wait for it: + + + +```kotlin +// Imports the kotlin.time.Duration to enable expressing duration in milliseconds +import kotlin.time.Duration.Companion.milliseconds + +import kotlinx.coroutines.* -suspend fun doWorld() = coroutineScope { // this: CoroutineScope +suspend fun main() = coroutineScope { + // Starts a coroutine that runs without blocking the scope launch { - delay(1000L) - println("World!") + // Delays to simulate background work + delay(100.milliseconds) + println("Sending notification in background") } - println("Hello") + + // Main coroutine continues while a previous one is delayed + println("Scope continues") } -//sampleEnd ``` -{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} - -> You can get the full code [here](https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-basic-03.kt). -> -{style="note"} +{kotlin-runnable="true"} + +After running this example, you can see that the `main()` function isn't blocked by `.launch()` and keeps running other code while the coroutine works in the background. -This code also prints: +### CoroutineScope.async() -```text -Hello -World! +The [`CoroutineScope.async()`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html) coroutine builder function is an extension function on `CoroutineScope`. +It starts a concurrent computation and returns a [`Deferred`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/) handle that represents an eventual result. +Use the `.await()` function to suspend the code until the result is ready: + +```kotlin +// Imports the kotlin.time.Duration to enable expressing duration in milliseconds +import kotlin.time.Duration.Companion.milliseconds + +import kotlinx.coroutines.* + +suspend fun main() = withContext(Dispatchers.Default) { + // Starts downloading the first page + val firstPage = async { + delay(50.milliseconds) + "First page" + } + + // Starts downloading the second page in parallel + val secondPage = async { + delay(100.milliseconds) + "Second page" + } + + // Awaits both results and compares them + val pagesAreEqual = firstPage.await() == secondPage.await() + println("Pages are equal: $pagesAreEqual") +} ``` +{kotlin-runnable="true"} - +### runBlocking() -## Scope builder and concurrency +The [`runBlocking()`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html) coroutine builder function creates a coroutine scope and blocks the current [thread](#comparing-coroutines-and-jvm-threads) until +the coroutines launched in that scope finish. -A [coroutineScope][_coroutineScope] builder can be used inside any suspending function to perform multiple concurrent operations. -Let's launch two concurrent coroutines inside a `doWorld` suspending function: +Use `runBlocking()` only when there is no other option to call suspending code from non-suspending code: ```kotlin +// Imports the kotlin.time.Duration to enable expressing duration in milliseconds +import kotlin.time.Duration.Companion.milliseconds + import kotlinx.coroutines.* -//sampleStart -// Sequentially executes doWorld followed by "Done" -fun main() = runBlocking { - doWorld() - println("Done") +// A third-party interface we cannot change +interface Repository { + fun readItem(): Int } -// Concurrently executes both sections -suspend fun doWorld() = coroutineScope { // this: CoroutineScope - launch { - delay(2000L) - println("World 2") - } - launch { - delay(1000L) - println("World 1") +object MyRepository : Repository { + override fun readItem(): Int { + // Bridges to a suspending function + return runBlocking { + myReadItem() + } } - println("Hello") } -//sampleEnd + +suspend fun myReadItem(): Int { + delay(100.milliseconds) + return 4 +} ``` -{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} - -> You can get the full code [here](https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-basic-04.kt). + +## Coroutine dispatchers + +A [coroutine dispatcher](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/#) controls which thread or thread pool coroutines use for their execution. +Coroutines aren't always tied to a single thread. +They can pause on one thread and resume on another, depending on the dispatcher. +This lets you run many coroutines at the same time without allocating a separate thread for every coroutine. + +> Even though coroutines can suspend and resume on different threads, +> values written before the coroutine suspends are still guaranteed to be available within the same coroutine when it resumes. +> +{style="tip"} + +A dispatcher works together with the [coroutine scope](#coroutine-scope-and-structured-concurrency) to define when coroutines run and where they run. +While the coroutine scope controls the coroutine's lifecycle, the dispatcher controls which threads are used for execution. + +> You don't have to specify a dispatcher for every coroutine. +> By default, coroutines inherit the dispatcher from their parent scope. +> You can specify a dispatcher if you need to run a coroutine in a different context. > {style="note"} -Both pieces of code inside `launch { ... }` blocks execute _concurrently_, with -`World 1` printed first, after a second from start, and `World 2` printed next, after two seconds from start. -A [coroutineScope][_coroutineScope] in `doWorld` completes only after both are complete, so `doWorld` returns and -allows `Done` string to be printed only after that: +The `kotlinx.coroutines` library includes different dispatchers for different use cases. +For example, [`Dispatchers.Default`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html) runs coroutines on a shared pool of threads performing work in the background, +separate from the main thread. -```text -Hello -World 1 -World 2 -Done -``` +To specify a dispatcher for a coroutine builder like `.launch()`, pass it as an argument: - +```kotlin +suspend fun runWithDispatcher() = coroutineScope { + launch(Dispatchers.Default) { + println("Running on ${Thread.currentThread().name}") + } +} +``` -## An explicit job +Alternatively, you can use a `withContext()` block to run all code in it on a specified dispatcher. -A [launch] coroutine builder returns a [Job] object that is a handle to the launched coroutine and can be -used to wait for its completion explicitly. -For example, you can wait for the completion of the child coroutine and then print the "Done" string: +The following example runs on `Dispatchers.Default`, which is optimized for CPU-intensive operations like data processing: ```kotlin +// Imports the kotlin.time.Duration to enable expressing duration in milliseconds +import kotlin.time.Duration.Companion.milliseconds + import kotlinx.coroutines.* -fun main() = runBlocking { -//sampleStart - val job = launch { // launch a new coroutine and keep a reference to its Job - delay(1000L) - println("World!") +suspend fun main() = withContext(Dispatchers.Default) { + println("Running withContext block on ${Thread.currentThread().name}") + + val one = async { + println("First calculation starting on ${Thread.currentThread().name}") + val sum = (1L..500_000L).sum() + delay(200L) + println("First calculation done on ${Thread.currentThread().name}") + sum + } + + val two = async { + println("Second calculation starting on ${Thread.currentThread().name}") + val sum = (500_001L..1_000_000L).sum() + println("Second calculation done on ${Thread.currentThread().name}") + sum } - println("Hello") - job.join() // wait until child coroutine completes - println("Done") -//sampleEnd + + // Waits for both calculations and prints the result + println("Combined total: ${one.await() + two.await()}") } ``` -{kotlin-runnable="true" kotlin-min-compiler-version="1.3"} - -> You can get the full code [here](https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-basic-05.kt). -> -{style="note"} +{kotlin-runnable="true"} -This code produces: +To learn more about coroutine dispatchers, including other dispatchers like [`Dispatchers.IO`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-i-o.html) and [`Dispatchers.Main`](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html), see [Coroutine context and dispatchers](coroutine-context-and-dispatchers.md). -```text -Hello -World! -Done -``` +## Comparing coroutines and JVM threads - +While coroutines are suspendable computations that run code concurrently like threads on the JVM, they work differently under the hood. -## Coroutines are light-weight +A _thread_ is managed by the operating system. Threads can run tasks parallel on multiple CPU cores and represent a standard approach to concurrency on the JVM. +When you create a thread, the operating system allocates memory for its stack and uses the kernel to switch between threads. +This makes threads powerful but also resource-intensive. +Each thread usually needs a few megabytes of memory, and typically the JVM can only handle a few thousand threads at once. -Coroutines are less resource-intensive than JVM threads. Code that exhausts the -JVM's available memory when using threads can be expressed using coroutines -without hitting resource limits. For example, the following code launches -50,000 distinct coroutines that each waits 5 seconds and then prints a period -('.') while consuming very little memory: +On the other hand, a coroutine isn't bound to a specific thread. +It can suspend on one thread and resume on another, so many coroutines can share the same thread pool. +When a coroutine suspends, the thread isn't blocked, and it remains free to run other tasks. +This makes coroutines much lighter than threads and allows running millions in one process without exhausting system resources. + +![Comparing coroutines and threads](coroutines-and-threads.svg){width="700"} + +Let's look at an example where 50,000 coroutines each wait five seconds and then print a period (`.`): ```kotlin +// Imports the kotlin.time.Duration to express duration in seconds +import kotlin.time.Duration.Companion.seconds + import kotlinx.coroutines.* -fun main() = runBlocking { - repeat(50_000) { // launch a lot of coroutines +suspend fun main() = coroutineScope { + // Launches 50,000 coroutines that each wait five seconds, then print a period + repeat(50_000) { launch { - delay(5000L) + delay(5.seconds) print(".") } } } ``` {kotlin-runnable="true" kotlin-min-compiler-version="1.3"} - -> You can get the full code [here](https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-basic-06.kt). -> -{style="note"} - +Now let's look at the same example using JVM threads: + +```kotlin +import kotlin.concurrent.thread -If you write the same program using threads (remove `runBlocking`, replace -`launch` with `thread`, and replace `delay` with `Thread.sleep`), it will -consume a lot of memory. Depending on your operating system, JDK version, -and its settings, it will either throw an out-of-memory error or start threads slowly -so that there are never too many concurrently running threads. +fun main() { + repeat(50_000) { + thread { + Thread.sleep(5000L) + print(".") + } + } +} +``` - - +Running this version uses much more memory because each thread needs its own memory stack. +Depending on your operating system, JDK version, and settings, +it may either throw an out-of-memory error or slow down thread creation to avoid running too many threads at once. -[launch]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html -[delay]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html -[runBlocking]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html -[CoroutineScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html -[_coroutineScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html -[Job]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html +## What's next - +* Discover more about combining suspending functions in [Composing suspending functions](composing-suspending-functions.md). +* Learn how to cancel coroutines and handle timeouts in [Cancellation and timeouts](cancellation-and-timeouts.md). +* Dive deeper into coroutine execution and thread management in [Coroutine context and dispatchers](coroutine-context-and-dispatchers.md). +* Learn how to return multiple asynchronously computed values in [Asynchronous flows](flow.md)