Table of content:

What is Multitasking?

What is a thread in Java?

What is thread Synchronization in java?

Different states of a Thread in Java

Deadlock of threads in java





Threads in Java - A Complete Guide


When several activities can be made to occur concurrently on the computer then it is termed Multitasking.
There are two types of multitasking:

1. Process-based multitasking
2. Thread based multitasking

In process-based multitasking, several processes (programs) run concurrently on the computer. For example - running a word processor and notepad or spreadsheet at the same time on the computer. Whereas in thread-based multitasking, parts of the same program can run concurrently. for example A word processor can edit and print the texts at the same time. Java supports thread-based multitasking and provides various facilities for multithread programming.



What is a thread in java?


A thread is a sequential path of execution within the same program. A program can have multiple threads that can run concurrently and each thread in the program can perform independent tasks as programmed. Every thread shares the same address space. A Java thread can be created by creating an object of java.lang.Thread class. Another way to create a thread in Java is to implement java.lang.Runnable interface.
Thread implementation by extending Thread class:



Implementation of thread by extending the Thread class

Here start() method is needed to invoke the thread, whereas the run() method defines the operation that a thread must perform. When a standalone application executes, it automatically creates a user thread which ultimately executes the main() method of Java. This is called the main thread. If there are no other threads implemented, then the user thread remains alive as long as the main() method is running. If other threads are implemented then they are called child threads. All these child threads originate from the main thread. These threads keep running even if the execution of the main() method finishes. This means the execution of the program continues until all the threads finish their execution.
Java virtual machine (JVM) distinguishes between the threads as user thread and daemon thread. A thread is considered a user thread as long as it is alive. A daemon thread is at the mercy of JVM and can be stopped by JVM when no more user threads are running. A user can mark the state of a thread as either daemon or user by a method called setDaemon(boolean). The state of a thread using setDaemon(boolean) can only be set before the thread is started. Any call to setDaemon(boolean) after the thread is started can throw IllegalThreadStateException.



What is thread Synchronization in java?


As we know Threads in Java share the same memory space i.e. they share the resources. In the absence of proper synchronization among threads, the integrity of program data might be compromised. There are instances where we want one thread at a time to have access to the shared resources. E.g. a banking account where debit and credit operations are to be performed, but without synchronization among user threads where all the user threads performing debit and credit operations concurrently can jeopardize the integrity of account data.

To achieve Synchronization, the thread must acquire the lock of the object. Every object in Java has a lock (also called a monitor). A lock is used to synchronize access to a shared resource. For a thread to access a shared resource, a thread must acquire the object lock associated with a shared resource. The runtime environment will make sure that no other thread can enter a shared resource if another thread already holds the object lock associated with the shared resource. Once the thread exits the shared resource, the object lock associated with the shared resource must also be freed by the runtime environment so that another thread waiting to access the shared resource can acquire the object lock and use the resource. The synchronized keyword is used either with a method or with a block so that the threads wishing to execute these blocks or methods can do so in a synchronized manner. i.e. a method or block declared as Synchronized can be accessed by only one thread at a time (the one which has acquired the lock of the object).

The below image depicts a code snippet having a synchronized method. Two threads get initiated at but only one thread can access the synchronized method at a time. The other thread gets access to the synchronized method only after the first one finishes.



Two threads can access a synchronized method only one at a time


Consider the case when the same method was there but without the keyword 'synchronized'. In that case, the output would have been like the one shown below. Both the threads are getting access to the method concurrently and thereby the integrity of the value of the variable 'var1' is compromised.




What are the different states of a Thread in Java?


A thread in Java has a life cycle. A thread goes through various states which defines the life cycle of a thread. A call to start() method does not necessarily mean that it has acquired the CPU and started its execution.

Ready to run: It means the thread has acquired life and is ready to run

Running: The thread is executing.

Dead: It means, the thread is done with the run() method (i.e. the thread has completed its execution) and now it can never come to life again even if the start() method is called again.

Non-runnable states: A running thread can acquire any of the non-runnable states.

Sleeping: A running thread sleeps for a specified amount of time and again acquires a ready-to-run state once the sleeping time is over. During sleep time (while the thread is sleeping), the thread continues to hold any lock that it might have acquired before coming to a sleeping state. The static method sleep() can be called to transit a running thread into a sleep state. Thread.sleep()

Blocked for I/O: A running thread can be transitioned into a Blocked for I/O state by executing a blocking operation requiring a resource (E.g. a call to an I/O method). In this state, a thread is waiting for a blocking operation to complete.

Blocked for join completion: A thread waiting for completion of another thread so that the other thread can join the first thread. For instance, thread t1 can invoke join() on thread t2. If t2 is still alive when join() was invoked upon it, then t1 transits to the Blocked for join completion state. If t2 has already completed its execution, then invoking the join() method will not affect the state of t1.

Waiting for notification: In this state, a thread is waiting for some notification from another thread. Waiting and notification are ways of communication among threads that are accessing synchronized methods. Wait and notify are the final methods defined in the Object class.

final void wait(long timeout)
final void wait(long timeout, int Nanos)
final void wait()
final void notify()
final void notifyAll()

These final methods can only be invoked on an object whose lock has been held by the thread, otherwise, the call will throw IllegalMonitorStateException.
When a thread invokes the wait() method on an object whose lock it holds, the thread is added to the wait set of the object and the thread transits to waiting for notification state. Whereas a thread invokes a notify() method on an object whose lock it holds, to notify the thread(s) that are in the wait set of the object. Once notified, a waiting thread first transits to the Blocked for lock acquisition state to acquire the lock on the object, and not directly to the ready-to-run state. The thread also gets removed from the wait set of the object.

Blocked for lock acquisition: In this state, a thread waits to acquire the lock of an object. A waiting thread upon being notified transits into the Blocked for lock acquisition state before acquiring the lock of an object. And then it goes to a Ready-to-run state.







What is the Deadlock of threads in Java?


A deadlock in Java is a situation when a thread is waiting to acquire an object lock that is being held by another thread, whereas this second thread is waiting to obtain an object lock being held by the first thread i.e. thread t1 waiting to obtain the object lock being held by thread t2, whereas t2 is waiting to acquire the object lock held by thread t1. Since each thread is waiting for the other thread to give up a lock, they both remain to wait forever in a Blocked-for-lock-acquisition state.