Assalaamu ‘alaikum and a warm welcome!

Bismillah.

In almost any Java or Android position interview, it is possible that the interviewer may ask you to differentiate Coroutines from Threads, or vice-versa. As a budding programmer, I would have mentioned that their function was the same; that they typically work in the background to execute tasks that would have otherwise cripple the main thread i.e. the user interface.

As far as my limited knowledge was concerned, there isn’t much difference between the two. Save for the fact that, one would have to code them differently because the syntaxes are different. Obviously. Coroutines are implemented in Kotlin. Threads, on the other hand, are the thing amongst Java programmers (although we do have them in Kotlin as well).

However, recently, the questions that often pop-up during interviews have been surprisingly consistent with regards to Coroutines and Threads. Alhamdulillah, with some time to spare (along with an accompanying gout in the feet), I explore these programming concepts with the reader.

What is Concurrency and Parallelism?

Before Co-routines and Threads are explained, please spare me a moment to explain about Concurrency and Parallelism. Often, for us programmers, when we say “do something in the background”,  we would often think of it as either a task being executed via a Coroutine or a Thread; some tasks that we delegate to another process. Often, from an academic perspective, such delegation is viewed as the notion of Concurrency and Parallelism.

Both Concurrency and Parallelism are concepts related to how multiple tasks or processes are executed simultaneously. If we were to draw a line between the two, Concurrency is all about managing multiple tasks, whereas Parallelism executes tasks simultaneously via multiple CPU cores.

I didn’t get that the first time. Let me polish that further.

Concurrency does not require multiple CPU cores in order to complete multiple tasks. It can be done in a single CPU and the trick to its ‘simultaneous’ performance is due to the CPU’s fast switching mechanism from one task to another.

Meanwhile, one would be able to achieve parallelism through multiple CPU cores. While one task is being executed in one core, another is being executed on another core. Hence, multiple tasks would run at the same time via these CPU cores.

How to Achieve Concurrency and Parallelism in Java or Kotlin?

Now that we know the gist of Concurrency and Parallelism, the next thing we need to know is how to implement them in both Java and Kotlin.

In Java, one can exhibit Concurrency by creating and managing threads:

  • Using the Thread class; or

  • Using the Runnable interface.

To achieve Parallelism in Java, one should resort to either Java’s:

  • Executor Framework;

  • Fork/Join Framework; or

  • Stream API (available in Java 8).

As for Kotlin, Concurrency can be implemented through the use of Coroutines.

If one opts for Parallelism in Kotlin, the way to go is either through:

  • Using the Thread constructor;

  • Using Coroutines with the awaitAll() function; or

  • Using the ExecutorService.

So, What Are The Differences Between Coroutines and Threads?

Now that we know their differences from a Concurrency and Parallelism perspective, we can simply note their individual differences as below:

  • How they execute: Threads use cooperative multitasking and run in parallel, while coroutines use cooperative multitasking in a single thread and make use of suspending functions to yield control when blocking operations occur,

  • How they utilize resources: Since Threads (in both Java and Kotlin) are akin to the concept of Parallelism, they are resource-intensive and use more memory than coroutines. Coroutines share the same thread and therefore is the complete opposite of Threads which run in multiple threads (memory space).

  • How they synchronize: Threads require synchronization mechanisms like locks, semophores, and monitors to facilitate safe concurrent access to shared resources. Coroutines use suspending functions and channels to prevent issues like data races, deadlocks, and livelocks.

  • How debugging is done: With Threads, debugging can be difficult because of its unpredictable behavior such as deadlocks, starvation, and race conditions. With coroutines, debugging is more straight forward because the code is sequential.

  • How they handle exceptions: In Threads, exceptions that occur in another thread have to be propagated to the main thread explicitly. The main thread is responsible for controlling the overall flow of the application and is the thread that initializes the other threads, so propagating the exception to the main thread ensures that the exception handling process is centralized and uniform. In coroutines, exceptions are automatically propagated to the calling routine.

Conclusively, it would seem that the benefits of using Coroutines far outweigh that if one were to use Threads. Coroutines provide an efficient and lightweight concurrency mechanism as opposed to Threads. Then again, the decision is left to the programmers to decide which approach is best for their applications.


Advertisement
Advertisement: Klikjer.com