Concurrency for Kotliners

Concurrency in Kotlin is the capability of a program to run multiple tasks at the same time.

Kotlin offers different methods to achieve concurrency, like threads, coroutines, and specialized classes known as concurrency primitives, like lock, atomic, and concurrent classes.

In this Answer, we will focus on threads and get introduced to coroutines.

Kotlin
Kotlin

Multithreading

Multithreading allows multiple parts of a program to run simultaneously, thus achieving concurrency.

Single thread

In the single-threaded approach, tasks are processed in a single sequence. This means that a particular task has to wait for its predecessor to be completed before it can be processed. Therefore:

  • Some processes would block others creating a “not so great” experience for our user (imagine if the UI thread is blocked for a while).
  • It takes a longer time for all tasks to be completed.
  • We are not utilizing our CPU to its full potential.

Multiple threads

In the multi-threaded approach, tasks are processed concurrently. This means a particular task does not have to wait for its predecessor to be completed before it can be processed. In multithreading, each part of the program is called a thread, and each thread has a unique execution path.

All threads in a process share resources (e.g., memory, data, etc.) Because of this, multithreading can be very economical as our threads share resources instead of accessing these resources in sequential order.

However, multithreading is complicated, and building safe multithreaded apps can be quite challenging.

How to use threads?

We create threads and run some parts of our programs in these threads. There are several ways to do this in Kotlin:

  • Extending the Thread class
class MyThread : Thread() {
public override fun run() {
//code to be executed in the new thread
println("Thread is running")
}
}
// ... some more code
fun main() {
// creating an instance of the class
val myThread = MyThread()
//start the thread, calling the run method
myThread.start()
// wait for the thread to finish
myThread.join()
}
  • Implementing the Runnable interface
class MyRunnableClass: Runnable {
public override fun run() {
// code to be executed in the new thread
println("Thread is running")
}
}
// ... some more code
fun main() {
// creating an instance of the class
val myRunnable = MyRunnableClass()
// creating a new thread and passing the runnable instance
val runnableThread = Thread(myRunnable)
//start the thread, calling the run method
runnableThread.start()
// wait for the thread to finish
runnableThread.join()
}
  • Using a lambda expression
fun main() {
val lambdaThread = Thread{
// code to be executed in the new thread
println("Thread is running")
}
// ... some more code
// starting the thread
lambdaThread.start()
// wait for the thread to finish
lambdaThread.join()
}

Coroutines

Traditional threads are costly to maintain and, as a result, are not as effective as we hope or imagine. However, Kotlin has coroutines that take care of the costs and complications of parallel programming (or concurrency). They are lightweight, efficient threads that are easy to use.

Coroutines now have a stable library, meaning there won't be any code-breaking changes to their API.

Advantages of using coroutines

  • Coroutines are lightweight and very efficient.

  • Coroutines can significantly improve performance.

  • They also make code cleaner and easier to write/read.

Attributions:
  1. undefined by undefined
Copyright ©2024 Educative, Inc. All rights reserved