instance of a program running on a computer. • It is a self-contained execution environment and typically has its own memory space. • Processes are independent of each other • Thread • A thread is a smaller sequence of programmed instructions within a process • Threads within the same process share the same memory space but operate independently • Multithreading allows a process to perform multiple tasks concurrently
sequential flow of execution of tasks of a process in an operating system (OS). • OS threads are at the core of Java’s concurrency model • Java threads are just a wrapper at OS thread • JVM must work with the underlying OS to create and manage that thread, which is quite expensive because the JVM must communicate with the OS back and forth throughout the thread’s lifetime • This switching is an expensive operation, which makes threads expensive
language • Do not require communication with the underlying OS to be created and managed • Coroutines are lightweight and can be launched millions at once • Java 19 introduced the concept of virtual threads. Virtual threads are lightweight threads that are managed by the JVM rather than the OS
thread • Coroutines can be • Creating async tasks only one thread (like js) – giving illusion of simultaneous execution. • Creating async tasks using thread pool • Dispatchers.IO, Dispatchers.Default • Default thread pool amount is the number of CPU cores • Runtime.getRuntime().availableProcessors()
runBlocking { launch { repeat(10) { println("Thread name: ${Thread.currentThread().name}: $it") delay(1000) } } launch { repeat(10) { println("Thread name: ${Thread.currentThread().name}: $it") delay(1000) } } } println("Thread name: ${Thread.currentThread().name}") } Only one thread is used! Runblocking scope is for main method or testing, it waits that all coroutines are done and then ends the app
{ println("A ${Thread.currentThread().name}: $it") delay(1000) } } launch(Dispatchers.Default) { repeat(100) { println("B ${Thread.currentThread().name}: $it") delay(1000) } } } } It will do some multitasking now
: List<Job> = List(10) { launch(Dispatchers.Default) { repeat(10) { println("${Thread.currentThread().name}: $it") delay(200) } } } jobs.forEach { it.join() } !// Wait for all jobs to complete println("done") } } Waits until the job is done
async(Dispatchers.Default) { var sum = 0 repeat(10) { sum += it delay(100) } sum } val result : Int = coroutine.await() println(result) } } In lambda, the last expression value in the block is return value Will wait until it receives the result Will hold the sum
async(Dispatchers.Default) { delay(100) 1 } val coroutine2: Deferred<Int> = async(Dispatchers.Default) { delay(300) 2 } val list : List<Deferred<Int!>> = listOf(coroutine1, coroutine2) val results : List<Int> = list.awaitAll() println(results) } } Waits for all to be complete
= List(100) { async(Dispatchers.Default) { delay(2000) (0!..10).random() } } val results : List<Int> = coroutines.awaitAll() println(results) } } Uses thread pool Will wait until it receives the result
that governs coroutine behavior. It typically includes: • Job -> can control lifecycle, like cancel • Dispatcher -> what thread? • CoroutineName -> optional name for debugging
Kotlin that • can pause its execution without blocking the underlying threa • Allows other tasks to run concurrently. • Suspend function usually has a suspend point. • When a suspend function reaches a suspension point • it saves its state and later resumes execution once the operation is complete • all without halting the progress of the thread it's running on.
suspends execution and allows other coroutines to run in the meantime. Common suspend points include: • delay • withContext(!..) • await() • calling another suspend function. • Rule: A suspend function must always be called inside a coroutine context • runBlocking • launch or async • Another suspend function
String { delay(1000) return "Hello after delay" } fun main() { runBlocking { val r = doSomething() } } Suspend function 'suspend fun delay(timeMillis: Long): Unit' should be called only from a coroutine or another suspend function.
doSomething(): String { delay(1000) !// Suspend point (coroutine pauses and resumes after 1 second) return "Hello after delay" } fun main() { runBlocking { val r = doSomething() println(r) } } suspend tells the compiler that this function may pause execution at certain points (delay, withContext, network calls, etc.) and resume later. These points are not allowed in normal functions.
suspend functions like copyFile(!!...) is to offload heavy or blocking work off the main (UI) thread. • The main thread in Android handles UI rendering, touch events, animations, etc. • If you do blocking operations (like Files.readString(...)) on the main thread, Android will throw NetworkOnMainThreadException or ANR (Application Not Responding). • By wrapping file I/O in withContext(Dispatchers.IO), you:
import java.util.concurrent.CompletableFuture fun main() { !// Create the HttpClient val httpClient: HttpClient = HttpClient.newHttpClient() !// Build the HttpRequest for the Chuck Norris API val httpRequest: HttpRequest = HttpRequest.newBuilder() .uri(URI.create("https:!//api.chucknorris.io/jokes/random")) .GET() .build() !// Send the asynchronous request and process the response val futureResponse: CompletableFuture<HttpResponse<String!>> = httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString()) futureResponse .thenApply { response: HttpResponse<String> -> val responseBody: String = response.body() !// Extract response body responseBody } .thenAccept { responseBody: String -> println("Response: $responseBody") !// Print the result } .join() !// Wait for completion }
HttpClient.newHttpClient() !// Build the HttpRequest for the Chuck Norris API val request = HttpRequest.newBuilder() .uri(URI.create("https:!//api.chucknorris.io/jokes/random")) .GET() !// Use the GET method .build() !// Send the asynchronous request and process the response var completableFuture = client.sendAsync (request, HttpResponse.BodyHandlers.ofString()) println("Start") completableFuture.thenApply { it.body() !// let's get the body of the http response }.thenAccept { println(it) !// and then print it }.join() } callbacks
makes the fetchChuckNorrisJoke() function suspend until the HTTP request completes, !// allowing other coroutines to run in the meantime. suspend fun fetchChuckNorrisJoke(): String { !// suspendCancellableCoroutine: Converts a callback-based async operation into a suspend function. !// The coroutine suspends until continuation.resume(result) is called. val value = suspendCancellableCoroutine { continuation -> val client = HttpClient.newHttpClient() val request = HttpRequest.newBuilder() .uri(URI.create("https:!//api.chucknorris.io/jokes/random")) .GET() .build() client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) .thenApply { it.body() }.thenAccept { !// Resumes the suspended coroutine when the HTTP request completes. !// Passes the joke (response body) as the function’s return value. continuation.resume(it) } } return value }
fetchChuckNorrisJoke() } val jokeDeferred2 = async { fetchChuckNorrisJoke() } !// Wait for both operations to complete and get their results. val joke1 = jokeDeferred1.await() val joke2 = jokeDeferred2.await() !// Print the results. println("Joke 1: $joke1") println("Joke 2: $joke2") } }
and callbacks • With suspendCancellableCoroutine it is possible to turn the function into a suspend function • If function does not have threading built in, you can use withContext