Like seriously , How Are Apps Able To Do More Than One Thing ?

Matts Makhura
7 min readApr 20, 2021
Multiple computations are happening at the same time

Have you ever wondered how your PC (smartphone, tablet, laptop or computer ) applications can do more than one thing at the same time ? Well in this post we will be exploring and defining the basics of this concept.

These days we have come to naturally expect a PC to have the ability to perform multiple tasks at the same time- well at least that’s what it looks like to our very divinely engineered eyes. However the PC has never been intelligent enough to do that but instead smart enough to make progress during the same period of time. This engineering illusion is called “Concurrency”

The word concurrency is derived from the word concurrent which is inherently derived from the Latin word concurrere meaning “to run together”.

Now if we were to expand this meaning into one of the bantu languages of Southern Africa— Sesotho Sa Leboa. Concurrent basically translates to “ho dira dilo mmoho“.

Let’s take this bantu translation and apply it to a real life example where your Grandmother has tasked you to go plough soil and while you are in the farm field you must remember to give your donkey water ; it’s sort of impossible to do these tasks precisely at the same time but it is possible to do these tasks together by switching between the multi-tasks according to what is needed the most at that period of time.

The same principle applies to computer programs when they are said to be concurrent — they switch between tasks so frequently such that it appears that those tasks are being executed simultaneously together. In our case it would appear that we are ploughing and giving the donkey water to stay hydrated at the same time but we are not.

Process Vs Threads

We can now paint the idea that computer programs are made up of two basic units — processes and threads.

Threads translates into , in the context of software engineering , “tshebetso” in Sesotho Sa Leboa.

Many Threads grouped into one Process would be “di’tshebetso”.

On a technical high level, a program needs memory and various other systems resources to execute.

A process would then be an independent execution environment that has a complete, private set of basic run-time resources and its own memory. On the other hand a thread can be considered as a mini-inner process that is dependent on the resources that its parent process poses. Threads generally exits within processes and require fewer resources to execute.

Going back to our analogy — ploughing is the process that contains:

  1. A Thread where the donkey is assisted by the human to plough
  2. Another thread where the human gives the donkey water to stay hydrated.
A young woman assisting her donkey to plough

How is a thread technically created using JAVA

Creating a thread in JAVA can is commonly done in two ways :

  1. By implementing Runnable interface — This interface defines a single method, run, meant to contain the code executed in the thread. The Runnable object is passed to the Thread constructor (See code below).
  2. By extending Thread class — This class itself implements Runnable
public class ThobelaRunnable implements Runnable {     public void run() {      System.out.println("Thobela from a thread!");     }     public static void main(String args[]) {      (new Thread(new ThobelaRunnable())).start();     }}

The first implementation, Runnable, is more generally used because the Runnable object can subclass a class other than Thread. The other reason is that your application wont be limited to be a descendant of Thread.

This first implementation invokes a new thread to start by calling “Thread.start()”.

Different States of a Thread

Going back to our analogy of ploughing ; what happens when our donkey gets dehydrated and needs water ? Naturally you would stop the donkey from ploughing and focus your will power on getting the donkey water.

The same applies in threading — we use “Thread.sleep()” to suspend execution of the current thread for a specified period. This way we efficiently make processing time available to other threads of an application or other applications that might be running on the computer system.

The method above can be used for different use cases such as pacing and waiting for another thread with duties that are understood to have time requirements. Example code can be found here.

“Thread.sleep()” is one of many methods used in thread states. Here is a summarized table that shows the different states of a thread :

Daemon threads

Another type of thread in JAVA is a daemon thread. This type of thread is a slave thread because its life depends on the master user thread. Its main purpose is to provide various services to the user threads for background supporting tasks. It has no role in life than to serve the user thread.

In our analogy a daemon thread could be a case where the human has to check if the water bucket has been filled and switch off the water tap so that the water does not overflow- also as soon as the donkey gets re-hydrated we no longer need to refill the bucket with water cause we are now ready to perform our original thread (guiding the donkey to plough).

Furthermore a technical use case of a daemon thread in JAVA is when your program needs to perform asynchronous I/O tasks or when it needs to listen for incoming connections. These tasks are fully dependent on the current user thread that is running and terminates as soon as that user thread ends.

The technical creation of a daemon thread follows the same principles of creating a normal thread with the only difference that it is created inside a user thread and that setDaemon(true) method has to be called before invoking it to start().

This link provides a good example of creating a daemon thread.

JMM

Now that we have briefly explored processes, the different use cases of threads and how these operate together ; you might be asking yourself where and how is all this stored in JAVA.

This is where the Java Memory Model comes in. This model helps us understand how the Java Virtual Machine works with the PC’s memory RAM.

The diagram below summarizes this relationship and more lower level details about JMM can be found here.

Deadlock, Starvation and Livelock

There are times when two or more threads are waiting for each other and the waiting never ends. Hence the two threads can’t complete their tasks. This concept is called DEADLOCK.

Another concept similar to deadlock with the difference that waiting time is infinite and that the threads will eventually get resources to execute their run()/start() method is called STARVATION.

In our analogy in order for us to run the “give the donkey water” thread while running the “ploughing” thread we have to wait for the donkey to stop performing optimally and realize that it is dehydrated for us to finally run the “give the donkey water” thread. This scenario could be an example of the starvation concept.

Another concept similar to deadlock is Livelock where you have two or more processes running concurrently and are continually repeating the same interaction in response to changes in the other processes without doing any useful work. Look at this example here.

Best practices

It’s important to note that if you don’t implement your thread using your runnable class — you must still override the run() method.

This is because that the compiler will not flash any error and it will execute the run() method of Thread class that has empty implemented. Therefore no output for this thread will exist.

Atomic Operations

There are times when you don’t want to execute something partially. If we go back to our analogy of the donkey in the field ploughing. The plough moves only when it is drawn by the donkey. This is potentially an example of a Atomic operation — which are operations that always execute together.

They happen instantaneously to the human eye (other threads) and do not suffer from the previously mentioned deadlocks. Read more here.

Java provides us with classes that are able to equip us to convert non-atomic operations into atomic operations by utilizing Atomic Variables. See the example below derived from here.

import java.util.concurrent.atomic.AtomicInteger;public class Atomicity {      AtomicInteger i;      public void incrementNumber() {           i.incrementAndGet();      }
}

The above code converts this “i= i+1” which is non -atomic to an atomic operation by using AtomicInteger Type “i” and method incrementNumber() to do “i+1” atomically.

Executor and ExecutorServices

Java has made thread creation relatively implementable and feasible by introducing the Executor Framework which allows reuse of the available thread instead of starting a new thread every time.

This Executor Framework provides a variety of built-in thread pools in Java, examples can be found here.

The ExecutorServices extends the Executor Framework interface with additional functionality to return Future objects and terminate or shut down the thread pool which the parent Executor Framework is incapable of achieving.

Concurrent Collection Classes

Java provides a very scalable and low risk way to deal with concurrent operations. This is possible through Concurrent Collection API’s. These classes provide similar functionality as the Collection Framework with the ability to support concurrency making this Concurrent Collection API’s stand out. An overview of classes that are within the new concurrent collection framework can be found here.

Finally we can thank the engineers and scientists for observing this principle of concurrency in nature and making it feasible to implement through Java for us to have applications that can almost subconsciously do more than one thing at the same time.

Photo by Tim Mossholder on Unsplash

--

--

Matts Makhura

If I would compare myself to a rock it would be a “Diamond” because it’s flaws is what makes so specials and impactful. I am here to learn and grow.