Multi-Threading In Java

What is a Thread?

A thread is a stream of control within a program that can be executed independently. Hence, a single process can be comprised of numerous threads.

For instance, in a multithreaded process, there may be different tasks that are fully abled to be run concurrently, like printing the document and running the GUI at an instant of time. Have a clear idea with a below picture,

Multithreading in java

If a CPU only has one processor then only one thread is executed at a time and CPU jumps between different threads but on a multiprocessor system, several threads are executed at the same time. They do so by sharing certain resources that enables each thread to see the state of the program.

Threads have access to the data, address space and other resources of their parent process. But all threads have their own activation records, local variables, stack pointers and program counters.

The program’s first executed function is ‘main’ which is responsible for creating all other thread, hence, main can be considered the main thread.

Internal Thread running for below program by CPU.

public class ThreadsInJava
{
    //Main Thread
    public static void main(String[] args)
    {
        for (int i = 0; i <= 100; i++)
        {
            System.out.println("From Main Thread");
        }
    }
}

Let’s look at an example how a user thread needs to be initialized and called in Java.

//Defining first thread with task
//The task of this thread is to print the numbers from 0 to 100 times
class ThreadOne extends Thread
{
    @Override
    public void run()
    {
        for(int i = 0; i <= 100; i++)
        {
            System.out.println(i);
        }
    }
}
 
//Defining second thread with task
//The task of this thread is to print the numbers from 101 to 200
class ThreadTwo extends Thread
{
    @Override
    public void run()
    {
        for(int i = 101; i<= 200; i++)
        {
            System.out.println(i);
        }
    }
}
 
public class ThreadsInJava
{
    //Main Thread
    public static void main(String[] args)
    {
        //Creating first thread
        ThreadOne t1 = new Thread1();
        t1.start();
 
        //Creating second thread
        ThreadTwo t2 = new Thread2();
        t2.start();
    }
}

Thread Scheduling-Need for Synchronization

In order to appreciate the need for synchronization, it is critical to understand the process of thread scheduling in multithreaded programming. Java supports fixed-priority scheduling to schedule its thread which is a simple and deterministic algorithm. All the runnable threads are schedules based on their priority.

Java has a method called setPriority to set the priorities of the threads ranging between the constants, MIN_PRIORITY and MAX_PRIORITY.

Higher priority thread runs till it stops, finishes the task or its run method exists. Although the algorithm is rather simple.

It can lead to race conditions. One example of it is a selfish thread.

A selfish thread is a thread that never terminates voluntarily. Hence, other threads with lower priority never get a chance to run.

An example of selfish thread is given below:

public int counter = 1;
public void run( ) {
While (counter < 500000000){
	Counter++;
}
}

A time-sliced system can prevent this behavior by assigning a limited amount of processor time to equal priority threads. Time slicing is also offered by Thread class in Java.

It is to be noted that time slicing does not offer any control on the order of running threads. Time-sliced threads can run arbitrarily and on a multiprocessor system where multiple threads run at the same time and share process resources, there is elevated possibility of collision.

To tackle this challenge, Java provides ways to implement synchronized multithreaded programming. Check out the below picture to get a clear idea, of Multi-Threading and synchronized Multi-Threading.

Thread Scheduling in java

Thread Class in Java

The process of creating threads in Java is straightforward. There are two ways to do it:

  1. Extend thread class
Class multiThreading extends Thread 
{
Public void run( ){
System.out.print (“Simple Multithreaded Program”);
}
Public static void main (String args[ ] 
{
multiThreading thread1 = new multiThreading ( );
thread1.start( );
}
}

2. Implement runnable Interface

class multiThreading implements Runnable
 {
public void run( )
{
System.out.print(“Implementing interface”);
} 
Public static void main (String args[ ]
 {
multiThreading thread1 = new multiThreading ( );
thread1.start( );
}
}

The start( ) method in both code blocks belongs to Thread class and it is used to call run( ) to begin execution. In short, the start( ) method renders a thread runnable whereas run( ) method set its state to running.

Other useful methods in the Thread class are sleep( ), setPriority( ), isAlive( ), suspend(), resume( ), yield( ), stop( ) and destroy( ).

Synchronization

Simple multithreading offers scheduling of threads, methods to set and get priorities but to write a thread-safe program, it is crucial to understand synchronized multithreading. Since threads have access to same data structures at the same time, it is possible for different threads to jumble the data.

For instance, if two threads are trying to write your bank’s new balance and first thread writes it to 2000.0 and a second later, the second thread overwrites it to 10. The updating of balance variable can be considered a critical section in a code.

A critical section is a code that must be allowed to finish without any interruption till its completion.

To handle critical section, threads can lock the individual objects in Java. This can be easily done using synchronized keyword in Java and few other methods. The concept is based on ‘monitors’ came into picture.

 A monitor implements mutual exclusion hence alleviating the need to implement critical sections.

Synchronized Keyword

Java has the keyword synchronize to implement synchronized multithreading. There are two ways to control the actions of threads using the keyword:

  1. Synchronized Methods

It is often required to synchronize whole methods of a class to ensure that only one method alters the invariants of class at any given point in time.  A code example for a program where two threads accessing variables or setting them may cause a problem is given below. Making both functions synchronized prevents the unnecessary collision.

Let’s get a detail idea with an example below:

Class Calculate
{
Double B1, B2, B3;
Synchronized double add( ){
Return B1 + B2 + B3;
}
Synchronized void set(double b1,double b2,double b3 )
{
B1 = b1;
B2 = b2;
B3 = b3;
}

2. Synchronized Statement

Shared resources can be enclosed in a block of code to be synchronized. A general synchronized statement can be written as:

Synchronized (objectidentifier)
{
// shared resources
}

Where object Identifier is the reference to the object that a thread needs to acquire a lock on.

Block synchronization is preferred in Java because it enables the programmer to lock only the critical section of the code.

There are few limitations of synchronized keywords in Java. One is that it can only be used within the same JVM. If there are more than one JVM then a global lock is required to ensure thread safety.

In brief,

  • Multithreading enhanced the performance and lowers the execution time to a great extent. This result in improved and highly responsive applications, better utilization of resources and improved throughput.
  • Synchronized multithreading goes further to give programmer the ability to control the flow of action of threads.
  • Synchronized can degrade performance, hence, should be used when it is critical to control threads access to objects and shared resources.

Leave a Reply