Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Learning Concurrency in Kotlin

You're reading from   Learning Concurrency in Kotlin Build highly efficient and scalable applications

Arrow left icon
Product type Paperback
Published in Jul 2018
Publisher Packt
ISBN-13 9781788627160
Length 266 pages
Edition 1st Edition
Languages
Concepts
Arrow right icon
Author (1):
Arrow left icon
Miguel Angel Castiblanco Torres Miguel Angel Castiblanco Torres
Author Profile Icon Miguel Angel Castiblanco Torres
Miguel Angel Castiblanco Torres
Arrow right icon
View More author details
Toc

Table of Contents (11) Chapters Close

Preface 1. Hello, Concurrent World! 2. Coroutines in Action FREE CHAPTER 3. Life Cycle and Error Handling 4. Suspending Functions and the Coroutine Context 5. Iterators, Sequences, and Producers 6. Channels - Share Memory by Communicating 7. Thread Confinement, Actors, and Mutexes 8. Testing and Debugging Concurrent Code 9. The Internals of Concurrency in Kotlin 10. Other Books You May Enjoy

Concurrency in Kotlin

Now that we have covered the basics of concurrency, it's a good time to discuss the specifics of concurrency in Kotlin. This section will showcase the most differentiating characteristics of Kotlin when it comes to concurrent programming, covering both philosophical and technical topics.

Non-blocking

Threads are heavy, expensive to create, and limited—only so many threads can be created—So when a thread is blocked it is, in a way, being wasted. Because of this, Kotlin offers what is called Suspendable Computations; these are computations that can suspend their execution without blocking the thread of execution. So instead of, for example, blocking thread X to wait for an operation to be made in a thread Y, it's possible to suspend the code that has to wait and use thread X for other computations in the meantime.

Furthermore, Kotlin offers great primitives like channels, actors, and mutual exclusions, which provide mechanisms to communicate and synchronize concurrent code effectively without having to block a thread.

Chapter 6, Channels – Share Memory by Communicating, Chapters 7, Thread Confinement, Actors, and Mutexes, and Chapter 8, Testing Concurrent Code, will focus on the correct usage of channels, actors, mutexes, and more to correctly communicate and synchronize your concurrent code.

Being explicit

Concurrency needs to be thought about and designed for, and because of that, it's important to make it explicit in terms of when a computation should run concurrently. Suspendable computations will run sequentially by default. Since they don't block the thread when suspended, there's no direct drawback:

fun main(args: Array<String>) = runBlocking {
val time = measureTimeMillis {
val name = getName()
val lastName = getLastName()
println("Hello, $name $lastName")
}
println("Execution took $time ms")
}

suspend fun getName(): String {
delay(1000)
return "Susan"
}

suspend fun getLastName(): String {
delay(1000)
return "Calvin"
}

In this code, main() executes the suspendable computations getName() and getLastName() in the current thread, sequentially.

Executing main() will print the following:

This is convenient because it's possible to write non-concurrent code that doesn't block the thread of execution. But after some time and analysis, it becomes clear that it doesn't make sense to have getLastName() wait until after getName() has been executed since the computation of the latter has no dependency on the former. It's better to make it concurrent:

fun main(args: Array<String>) = runBlocking {
val time = measureTimeMillis {
val name = async { getName() }
val lastName = async { getLastName() }

println("Hello, ${name.await()} ${lastName.await()}")
}
println("Execution took $time ms")
}

Now, by calling async {...} it's clear that both of them should run concurrently, and by calling await() it's requested that main() is suspended until both computations have a result:

Readable

Concurrent code in Kotlin is as readable as sequential code. One of the many problems with concurrency in other languages like Java is that often it's difficult to read, understand, and/or debug concurrent code. Kotlin's approach allows for idiomatic concurrent code:

suspend fun getProfile(id: Int) {
val basicUserInfo = asyncGetUserInfo(id)
val contactInfo = asyncGetContactInfo(id)

createProfile(basicUserInfo.await(), contactInfo.await())
}
By convention, a function that is going to run concurrently by default should indicate this in its name, either by starting with async or ending in Async.

This suspend method calls two methods that will be executed in background threads and waits for their completion before processing the information. Reading and debugging this code is as simple as it would be for sequential code.

In many cases, it is better to write a suspend function and call it inside an async {} or launch {} block, rather than writing functions that are already async. This is because it gives more flexibility to the callers of the function to have a suspend function; that way the caller can decide when to run it concurrently, for example. In other cases, you may want to write both the concurrent and the suspend function.

Leveraged

Creating and managing threads is one of the difficult parts of writing concurrent code in many languages. As seen before, it's important to know when to create a thread, and almost as important to know how many threads are optimal. It's also important to have threads dedicated to I/O operations, while also having threads to tackle CPU-bound operations. And communicating/syncing threads is a challenge in itself.

Kotlin has high-level functions and primitives that make it easier to implement concurrent code:

  • To create a thread it's enough to call newSingleThreadContext(), a function that only takes the name of the thread. Once created, that thread can be used to run as many coroutines as needed.
  • Creating a pool of threads is as easy, by calling newFixedThreadPoolContext() with the size and the name of the pool.
  • CommonPool is a pool of threads optimal for CPU-bound operations. Its maximum size is the amount of cores in the machine minus one.
  • The runtime will take charge of moving a coroutine to a different thread when needed .
  • There are many primitives and techniques to communicate and synchronize coroutines, such as channels, mutexes, and thread confinement.

Flexible

Kotlin offers many different primitives that allow for simple-yet-flexible concurrency. You will find that there are many ways to do concurrent programming in Kotlin. Here is a list of some of the topics we will look at throughout the book:

  • Channels: Pipes that can be used to safely send and receive data between coroutines.
  • Worker pools: A pool of coroutines that can be used to divide the processing of a set of operations in many threads.
  • Actors: A wrapper around a state that uses channels and coroutines as a mechanism to offer the safe modification of a state from many different threads.
  • Mutual exclusions (Mutexes): A synchronization mechanism that allows the definition of a critical zone so that only one thread can execute at a time. Any coroutine trying to access the critical zone will be suspended until the previous coroutine leaves.
  • Thread confinement: The ability to limit the execution of a coroutine so that it always happens in a specified thread.
  • Generators (Iterators and sequences): Data sources that can produce information on demand and be suspended when no new information is required.

All of these are tools that are at your fingertips when writing concurrent code in Kotlin, and their scope and use will help you to make the right choices when implementing concurrent code.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime