Concurrency

·     What is the difference between process and thread?

If you are an absolute beginner in the field of software engineering (like me), there is a whole new vocabulary to learn starting from basic concepts like what is a process and what is a thread. Despite being seemingly basic, these terms pop up quite often during technical interviews. So, not to lose more of your precious time let’s try and dissect these two like two little “frogs” in your biology classes at school (sweet time 😊).

Processes and threads are the ground blocks of concurrency or in other words when something happens at the same time. When we browse the internet, we use a program to open websites (Mozilla, Chrome, etc.) and open several tags, so the use of a browser is a process and tags are the threads. It happens at the same time as a user experiences it; however, in reality, it is not. The CPU on the computer works that fast to create this illusion of simultaneously happening processes.

·    How can we create a thread and run it?

Mmm… Do you have an IDE? Like Eclipse or IntelliJ? If not, you need to get it 😊.

Then once you are done with the setup, check if it works, etc. we open Eclipse (I use it) and create a class (like Test, for example) to start.

Threads are created either by extending a class Thread or implementing an interface Runnable.

Java has its class called Thread, which comes with a bunch of useful functions. To access those functions, we need to build a connection to the Thread class. And that can be done by the magic keyword ‘extend’. So, you extend your created class to the Thread class. In other words, you created a “child” class that has the features of the “parent” class:

public class Test extends Thread {}

The parent class “Thread” has many functions but the one that initiates a new thread is run()

 (Like Run, Forest, run… 😊)

public class Test extends Thread {

@Overrride

public void run()} {

System.out.println(“T is running..”)

}

So now it’s supposed to work, right?

Oops, we forgot the main method for your own created class…Cause ‘run’ method belongs to the Thread, and in order to function, it needs something from the main class to act upon…An Object!

So, now let’s create the main method

public static void main(String[] args) {

Test thread = new Test();}

Now the object in the Test class can use the methods that belong to the Thread class! Magic!

And we are going to do just that: start() is the method to start the thread running

thread.start();

}

}

Voila!

Another way of creating a thread is implementing an interface called Runnable. We first create our class to test:

class RunnableIntTest

Then this class implements the Runnable interface and uses its only method run().

class RunnableInt Test implements Runnable 

{

@Overrride

public void run() 

{

System.out.println(“T is running”)

}

}

    After that we call the main method, create an object in our created class and then call the class Thread, create an object there and pass the object created in our class to the Thread().

 class RunnableIntTest

Then this class implements the Runnable interface and uses its only method run().

class RunnableInt Test implements Runnable 

{

@Overrride

public void run()

 {

System.out.println(“T is running”)

}

public static void main(String[] args)

{

RunnableInt rt = new RunnableInt();

Thread t1 = mew Thread (rt);

t1.start();

}  

According to the memory usage and OOP principles, the implementation of the interface is the better choice for creating threads. If you need to know more about this topic, take a look here: (https://howtodoinjava.com/java/multi-threading/java-runnable-vs-thread/)

Different states of a thread and transitions

Different states of a thread

The next step after we start a thread to run, we probably need to know what it is doing. There is a specific term for it called "states" of a thread. When we use run(), the state of the thread is NEW.       After start(), it changes to RUNNABLE. After invoking the method sleep(), the thread is in TIME-          WAITING state for the duration of time passed as an argument to the sleep() method. A thread is       WAITING when it waits for another thread to execute its function. The thread can also be TERMINATED. It happens either after the thread executed the function or if we called the method  stop().

Daemon thread

Daemon thread is a fancy name for another type of thread that we can create. Why is it called a daemon? As I googled the origin of the name it came back with an interesting explanation that can be found here (https://www.cs.cornell.edu/courses/JavaAndDS/files/ThreadsDaemon.pdf). Here we need to understand the purpose of this type of thread.

First of all, it runs in the background and performs functions like garbage collection in Java or memory release from unused objects, and removing unwanted entries from the cache. It does not prevent Java virtual machine from exiting. We can ask the question: “How does it do it?” Because it has low priority. Here you are, the mystery is solved… and another thing: since it does not prevent JVM from exiting it is sort of “forever running”; hence, to stop it, it must be killed (use the function stop()).

The way to create a daemon thread is the same as creating any thread (check out my blog “Some basics about processes and threads for beginners in Software Engineering”) and use the method setDaemon():

package javaThreads;

class Mytest implements Runnable{

            public void run() {

                        System.out.println("T is running");

            }

public static void main(String[] args) throws InterruptedException {

            Mytest rc = new Mytest();

            Thread t1 =  new Thread(rc);

            t1.start();

            Thread t2 = new Thread(rc);

            t2.setDaemon(true);

            t2.start();         

            System.out.println("t1 "+t1.getState());

            System.out.println("daemon: "+t2.getState());

            System.out.println(t2.isDaemon());

}

} 

Java Memory Model

JMM has quite a complicated structure. In my search, I found this informative graph (below) in one of the blogs, which nicely demonstrates how the JMM is built.

JMM is basically divided into two parts: JVM Heap and Perm (stands for permanent memory). JVM Heap in its turn consists of the “Young Gen” and “Old Memory”. Young Gen is split into three parts: “Eden”, “S0”, and “S1”.

Newly created objects are first saved in Eden. Once Eden is full, the first Garbage Collection (Minor GC) takes place. (Java is one of the platforms, where garbage collection happens automatically). After this check, all objects that passed the filters in during Minor GC are moved to one of the storages (S0 or S1) or survivor spaces. Minor garbage collections run regularly, checking Eden and one of the S0 or S1, whichever is full and moving the objects to empty one.  After a few cycles of minor GC, the worth saving objects are moved to the Old Generation Memory. GC in Old Gen memory occurs when this part of the memory is full, and it takes a significantly longer time. It is called Major GC and when it is triggered it is called a Stop the World Event; since all processes stop till the garbage collection is finished.

Permanent Generation is a separate part of memory that consists of objects collected after full GC. It contains metadata, library classes, and methods.

Credits: https://www.digitalocean.com/community/tutorials/java-jvm-memory-model-memory-management-in-java

https://journaldev.nyc3.digitaloceanspaces.com/2014/05/Java-Memory-Model.png

Deadlock, Livelock, and Starvation

  Deadlock, Livelock, and Starvation are the events that can happen during threads execution.

  As the name suggests deadlock means when something is not moving in any direction.

  There are 4 events that need to happen in order to create a deadlock scenario:

  •  Mutual exclusion
  •  Hold and Wait
  •  No preemption
  •  Circular wait

For example, we have a library that lends books. There is one book, which is represented only in one copy. There is no limit on time for borrowing books. If person A borrowed a book and holds it, another person B cannot check out the same book. Here we have a deadlock.

The resource is not shareable, one person holds a resource, and the other waits for it, there is no preemption (no limit on the borrow-time), and the process of checking out the book and waiting for the process of returning it first.

Livelock is a bit different from deadlock but only because the threads are executed as one being a response to the action of another; hence, the program does not proceed further since the process is circled.
Starvation happens when the higher priority thread prevents a lower priority thread from executing. High-priority threads are pushed to be executed in the first place, keeping the low-priority threads waiting indefinitely. 
 

What happens if one doesn't not override the class Thread run() method?

You have probably noticed that run() is overridden (it has keyword @Override). 

What happens if it is not?

It will execute the run() method but if there is nothing inside the curvy brackets {} there won't be any output.

What are atomic operations and atomic classes? 

Atom means “not divisible”. The word was borrowed from physics or chemistry sciences. The atomic operations are the ones that happen all at once (https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html) and it experiences no interference from other operations until it is done.

Atomic classes in Java are AtomicInt, AtomicLong, AtomicBoolean, and AtomicReference. They represent integer, long, Boolean, and object references. For example, if we have a counter that counts the number of elements in an array. In a multi-thread environment; since

 i++ in for loop like below:

    int counter = 0;

    for(int i=0, i<array.size(), i++)

        {

            counter = counter+=1;

     }

is not atomic, another thread may access it and read the length of the array wrong. To prevent it, atomic variables are used to make sure that the operation is not accessed by other threads before it is finished.

Executor and ExecutorService

Executor is an interface that was created to effectively manage threads in a multi-threaded environment.  There are a few types of Executor interfaces that can be implemented: SingleThreadExecutor, FixedThreadPool, CachedThreadPool, ScheduledExecutor.

Executor interface provides the functionality of creating a pool of threads interface as opposed to the Thread class, which creates a single thread at once. The process of creating a thread is a resource-consuming process and it makes a big difference to create a pool of threads at once that can be manipulated further with ExecuteService sub-interface.

ExecutorService is a sub-interface of Executor. ExecutorService is provided with functionality to manage the lifecycle of a multi-threaded application. Another addition ExecutorService has is submit() function which returns an object (Future), which produces the results in asynchronous  computations.

1.:https://javarevisited.blogspot.com/2017/02/difference-between-executor-executorservice-and-executors-in-java.html

2.  https://docs.oracle.com/javase/tutorial/essential/concurrency/collections.html

Concurrent Collection Classes

Even if you just began your journey in Software Engineering, you most probably heard about Collections. You maybe used them when you needed to access classes like ArrayList, LinkedList or HashMaps.  Concurrent Collection Classes have the same concept. They are structures that provide architecture to store and manipulate the group of objects. A Collection allows a user search, sort, manipulate, and delete data. It has classes and interfaces. The only difference is that they are synchronized in nature and preferably used when one needs to work with multiple threads, which makes the life of a programmer so much easier.

Concurrent Collections Classes:

BlockingQueue: it waits for the queue to have at least one element before retrieving and element or wait for the space become available before adding a new element.

ConcurrentMap is a sub-interface of java.util.Map guaranties atomicity (I rumble a bit about atomicity in the next paragraph, so, if you are not asleep yet, stick around for a bit longer) in operations like remove or replace a key-value pair only if the key is present or add a key-value pair only if the key is absent. The standard general-purpose implementation of ConcurrentMap is ConcurrentHashMap, which is a concurrent analog of HashMap.

ConcurrentNavigableMap is a sub-interface of ConcurrentMap that supports approximate matches. The standard general-purpose implementation of ConcurrentNavigableMap is ConcurrentSkipListMap, which is a concurrent analog of TreeMap.

Source: https://docs.oracle.com/javase/tutorial/essential/concurrency/collections.html


Comments