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) • Creating async tasks using thread pool • Dispatchers.IO, Dispatchers.Default • Default thread pool amount is the number of CPU cores • Runtime.getRuntime().availableProcessors()
for main method or testing, it waits that all coroutines are done and then ends the app launch { repeat(10) { println("A ${Thread.currentThread().name}: $it") delay(1000) } } launch { repeat(10) { println("B ${Thread.currentThread().name}: $it") delay(1000) } } } } Only one thread is used!
{ 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
async(Dispatchers.Default) { var sum = 0 repeat(10) { sum += it delay(100) Async will return a value! } sum } val result = coroutine.await() println(result) } } Will wait until it receives the result
= mutableListOf() repeat(100) { coroutines.add(async(Dispatchers.Default) { var sum = 0 println(Thread.currentThread().name) repeat(10) { sum += it delay(100) } sum }) } } } Uses thread pool val results : List<Int> = coroutines.awaitAll() println(results) Will wait until it receives the result
{ val thread = Thread { try { Thread.sleep(1000) // Simulate a slow disk so timings are visible val data = Files.readString(Path.of(path)); callback(null, data) // Success: no error, data present } catch (e: Exception) { callback(e, null) // Failure: error present, no data } } thread.start() return thread // Returned so callers can interrupt it later }
request = HttpRequest.newBuilder() .uri(URI.create("https://api.chucknorris.io/jokes/random")) .GET() .build() Changes the context to thread pool! May wait if thread pool is full return withContext(Dispatchers.IO) { println(Thread.currentThread().name) val response = client.send(request, HttpResponse.BodyHandlers.ofString()) if(response.statusCode() != 200) { throw RuntimeException("Failed to fetch joke, status code: ${response.statusCode()}") } response.body() } } fun main() { runBlocking { println(Thread.currentThread().name) val deferredList = List(10) { async { fetchRandomJoke() } } val jokes = deferredList.awaitAll() println(jokes) } } main thread Do asynchro nously 10 fetching
Java/Kotlin Java/Kotlin Java Coroutine Support Native support Coroutine wrappers available Native support with Coroutine Adapter Coroutine wrappers needed Asynchronous Model Built-in coroutines Callbacks/Futures Callbacks/Futures/Coroutines with Adapter Synchronous/blocking (Async with Threads) HTTP/2 Support Yes Yes Yes (via OkHttp) Yes (Java 9+) WebSocket Support Yes Yes No (Uses OkHttp) No Multiplatform Yes (JVM, JS, Native) JVM JVM JVM Serialization Built-in formats support No built-in support Converter libraries (e.g., Gson, Moshi) Manual or use third-party libraries Configuration/Usage DSL for configuration Builder pattern Interface annotations URLConnection API Community/Support Growing Large Large Ubiquitous Use Case Full-stack (client/server) Client HTTP requests Type-safe HTTP client Basic HTTP requests
API that can use different engines • Ktor uses Kotlin coroutines to handle concurrency • Ktor supports JVM, Android, JavaScript, and Native platforms • Ktor also includes a multiplatform HTTP client library that supports making asynchronous requests, streaming, and websockets, fully integrated with Kotlin coroutines.
You can use different http clients, CIO is Ktor's own val client = HttpClient(CIO) suspend fun apiRequest(url: String): String { // client.get suspends instead of blocking, so it is safe to call // from any thread and many requests can run concurrently val response: HttpResponse = client.get(url) return response.bodyAsText() } fun main() { try { runBlocking { repeat(10) { launch { val data = apiRequest("https://api.chucknorris.io/jokes/random") println(data) } } } } finally { client.close() } }
support Coroutine support with adapters Not specifically designed for coroutines, but can be used with them Coroutine support with adapters Kotlin Language Support Full support (Kotlin-first) Excellent with Kotlin extensions Good, but not Kotlin-first Good with Kotlin module Multiplatform Support Yes (JVM, JS, Native) No (JVM and Android-focused) No (JVM focused) No (JVM focused) Reflection Uses Kotlin's own serialization Uses reflection or codegen Primarily uses reflection Uses reflection or codegen Annotation Processing/Codegen Optional Required for optimal performance Not required Required for optimal performance Default Value Support Excellent Excellent with codegen Limited Excellent with Kotlin module Null Safety Fully supported Fully supported with codegen Limited Fully supported with Kotlin module Performance Very good Very good with codegen Good Very good with codegen Maven Central Artifact Yes Yes Yes Yes JSON to/from Data Class Yes Yes Yes Yes Project Maturity Mature, officially supported by JetBrains Mature, widely used in Android Very mature, widely used Very mature, widely used
ignoreUnknownKeys = true } // Create a Json instance with configuration @Serializable data class ChuckNorrisJoke( val value: String ) suspend fun fetchData(url: String): String { val response: HttpResponse = client.get(url) return response.bodyAsText() } fun parseJoke(jsonString: String): ChuckNorrisJoke { // ChuckNorrisJoke.serializer() => KSerializer<ChuckNorrisJoke>: an object that // knows how to convert a ChuckNorrisJoke to and from JSON (or another format). return json.decodeFromString(ChuckNorrisJoke.serializer(), jsonString) } fun main() { runBlocking { launch { val data = fetchData("https://api.chucknorris.io/jokes/random") val obj: ChuckNorrisJoke = parseJoke(data) println(obj.value) } } }
should only be executed once when the key (a specified dependency) changes. It runs the side effect code in a coroutine scope, allowing asynchronous work without blocking the UI. • This is useful for operations like making a network request, performing disk I/O, or any task that should be triggered when a specific value or dependency changes. • The side effect is launched in a coroutine and is independent of the Composable function’s recomposition.
to be acquired and released when a Composable is created and destroyed (disposed). • The effect code inside DisposableEffect runs when the Composable is first recomposed, and when the key (dependency) changes, it will dispose of the previous resource before running the effect code again. • This is useful for tasks like acquiring and releasing resources, such as registering and unregistering observers or listeners. • The side effect is executed every time the Composable is recomposed and is subject to recomposition.
} Column { Button(onClick = { checkedState = !checkedState }) { Text("toggle: $checkedState") } if(checkedState) { Clock() } } Invokes the setter and getter of value by default } @Composable fun Clock() { // Remember the current time state var time by remember { mutableStateOf(currentTime()) } // Use LaunchedEffect with Unit to start the effect immediately and only once LaunchedEffect(Unit) { while (true) { time = currentTime() // Update time every second println(time) delay(1000) // Wait for one second } } // Display the current time Text(text = time) } // Helper function to get the current time as a formatted String fun currentTime(): String = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")) Will leave it running, you could use DisposableEffect
Ktor - 2011 - - Kotlin is unveiled by JetBrains. 2012 OkHttp 1.0 is released by Square Inc. - 2013 - Retrofit 1.0 released, offering a REST client for Android and Java. 2014 OkHttp 2.0 released, introducing major updates and improvements. - - 2016 OkHttp 3.0 released, further improvements and features. 2017 Continuous enhancements and support for HTTP/2. 2018 - 2019 OkHttp 4.0 released, embracing Kotlin while maintaining Java compatibility. 2020 onwards Ongoing updates, focusing on performance and security enhancements. Retrofit 2.0 released with significant updates like a new call adapter mechanism. Updates and new features, including better error handling and RxJava support. - - Ktor development begins, leveraging Kotlin's capabilities. Ktor officially announced, providing a Kotlin-first approach to web and application development. Momentum grows, with significant updates enhancing functionality. Retrofit adds support for Kotlin Ktor 1.0 released, marking its maturity coroutines, enhancing its integration for production use. with modern development practices. Continuous improvement, maintaining Continuous updates and popularity and adding features to improvements, expanding support for support modern app development. multiplatform projects.
suitable for JVM • First release in 2013, idea was to create a better than the official HttpURLConnection • Features • Efficiency, Http/2, Connection pooling • Integration with Retrofit (very popular tool, coming later on) • 2019: 100% Kotlin support • https://square.github.io/okhttp/
val request = Request.Builder() .url("https://api.chucknorris.io/jokes/random") .build() val response: Response = client.newCall(request).execute() val result = response.body?.string() ?: "error" println(result) response.close() }
client = OkHttpClient() val request = Request.Builder() .url("https://api.chucknorris.io/jokes/random") .build() thread { client.newCall(request).execute().use { response -> callback(response.body?.string() ?: "Failed to fetch the joke - no response body") } } } fun main() { println("START on thread: ${Thread.currentThread().name}") fetchJoke { println("Joke fetched on thread: ${Thread.currentThread().name} - $it") // you could do another fetchJoke here -> callback hell } println("STOP on thread: ${Thread.currentThread().name}") }
thread pool to reuse • Handling errors in callbacks can be hard • Callback hell • Complex lifecycle management • If using with android, you cannot manage UI with worker thread
async fetch val joke1 : Deferred<String> = async(Dispatchers.IO) { fetchJoke() } // starts another async fetch val joke2 : Deferred<String> = async(Dispatchers.IO) { fetchJoke() } // Parallel fetching happening, will be waiting that both are ready println(joke1.await()) println(joke2.await()) } }
app ends after last coroutine exists runBlocking { val startTime = System.currentTimeMillis() val joke1 : Deferred<String> = async(Dispatchers.IO) { delay(2000) "hello" } val joke2 : Deferred<String> = async(Dispatchers.IO) { delay(3000) "world" } // both will run parallel, until the last on exists we are ready to go. println(joke1.await()) println(joke2.await()) } } val endTime = System.currentTimeMillis() println("Time taken: ${endTime - startTime} ms")
= mutableListOf<Deferred<String>>() // Launch 10 asynchronous fetches repeat(10) { val jokeDeferred: Deferred<String> = async(Dispatchers.IO) { fetchJoke() } jokes.add(jokeDeferred) } // Wait for all the jokes to be fetched and print them jokes.forEach { deferredJoke -> println(deferredJoke.await()) } }
"Hello" } fun main() { runBlocking { // starts async fetch val joke1 : Deferred<String> = async(Dispatchers.IO) { fetchJoke() } // starts another async fetch val joke2 : Deferred<String> = async(Dispatchers.IO) { fetchJoke() } } } // Parallel fetching happening, will be waiting that both are ready println(joke1.await()) println(joke2.await()) delay is a suspend function which can be called only within a suspend funcion This is rather ugly way of getting the result “Hello”
"Hello" } Delay allows other coroutines do there job fun main() { val startTime = System.currentTimeMillis() runBlocking { launch { val joke = fetchJoke() println(joke) } launch { val joke = fetchJoke() println(joke) } } } Runs parallel, notice the easy return value, only one thread is used! val endTime = System.currentTimeMillis() println("Time taken: ${endTime - startTime} ms") ~1 sec
System.currentTimeMillis() var value = -1 while(value != 5) { value = (1..1000000000).random() } val endTime = System.currentTimeMillis() println("Calc Time taken: ${endTime - startTime} ms") } return "result" fun main() { val startTime = System.currentTimeMillis() runBlocking { repeat(10) { launch { val result = calculateStuff() println(result) } } } } val endTime = System.currentTimeMillis() println("Time taken: ${endTime - startTime} ms") Because one thread is used, this is sync!
System.currentTimeMillis() var value = -1 while(value != 5) { value = (1..1000000000).random() } val endTime = System.currentTimeMillis() println("Calc Time taken: ${endTime - startTime} ms") } return "result" fun main() { val startTime = System.currentTimeMillis() runBlocking { repeat(10) { launch(Dispatchers.Default) { val result = calculateStuff() println(result) } } } } val endTime = System.currentTimeMillis() println("Time taken: ${endTime - startTime} ms") Now it is parallel
client = OkHttpClient() val request = Request.Builder() .url("https://api.chucknorris.io/jokes/random") .build() val value = withContext(Dispatchers.IO) { client.newCall(request).execute().use { response -> println("${Thread.currentThread().name}") return@withContext response.body?.string() ?: "Failed to fetch the joke - no response body" } } return value } This part will be done in IO thread pool.
client = OkHttpClient() val request = Request.Builder() .url("https://api.chucknorris.io/jokes/random") .build() return withContext(Dispatchers.IO) { client.newCall(request).execute().use { response -> response.body?.string() ?: "Failed to fetch the joke - no response body" } } } If no return, it will be automatically return for the outer lambda!
2019: Version 1.0 • Suitable for web applications, HTTP services, mobile, and browser apps • Uses Kotlin's coroutines for non-blocking I/O • Customizable through features and plugins
RxJava, or callbacks Asynchronous Support Native support through Kotlin coroutines HTTP Engine Multiplatform (CIO, OkHttp, Jetty, etc.) OkHttp Serialization Flexible, supports various formats through plugins Gson, Moshi, Jackson, or converters Multiplatform Yes (JVM, Android, JavaScript, Native, iOS) No (Focused on JVM and Android) HTTP 2 Support Yes Yes Websockets Support Yes Not natively, available through OkHttp Client/Server Both (Can be used to create client and server) Client only DSL for Configuration Yes, has a type-safe DSL for building HTTP clients No, uses annotations and interfaces Community and Support Growing, backed by JetBrains Well-established, large community Customizability Highly customizable and extensible Customizable with interceptors and converters Learning Curve Moderate, requires understanding of coroutines Easy to moderate, especially for Retrofit users
RESTful web services. It automatically handles request and response serialization and deserialization with minimal boilerplate code. • Type-safe HTTP client: • Allows for defining API interfaces in code, making API calls more intuitive and reducing the risk of errors. • Designed specifically for Android and Java: • Though it can be used in any Java application, its features and community support are particularly strong in the Android ecosystem. • Integration with OkHttp: • It uses OkHttp for the underlying HTTP communication, benefiting from OkHttp’s powerful features like connection pooling, GZIP compression, and caching.