Multithreading


The purpose of multithreading is to execute multiple thread in parallel or sequence as per the problem requirement.
So next question is what is thread?  Thread is a light weight process that has its own call stack.
In java there is one thread per call stack—or, to think in inverse one call stack per thread.
The main method that start the program runs in one thread, is called main thread.

We can define and instantiate a thread in two ways

1.      By extending the java.lang.Thread class
2.      By Implement the Runnable interface

Code snippet -

/**
 * Creating thread via extending java.lang.Thread class
 * @author MANOJ
 */
public class TestThread extends Thread {
     public void run() {
           System.out.println("thread is running...");
     }

     public static void main(String args[]) {
           TestThread testThread = new TestThread();
           testThread.start();
     }
}

/**
 * Creating thread via extending java.lang.Runnable Inteface
 * @author MANOJ
 */
public class TestRunnable implements Runnable {
     public void run() {
           System.out.println("thread is running...");
     }

     public static void main(String args[]) {
           TestRunnable testRunnable = new TestRunnable();
           Thread thread = new Thread(testRunnable);
           thread.start();
     }
}

As per the multiple inheritance limitation in java classes we prefer to use runnable interface for thread creation

Thread State and Transition –

A thread can be in 5 states.
1.    New - In this state a thread has been created, but the start method has not been invoked on the thread.
2.    Runnable- In this state thread is eligible to run but it’s waiting for the scheduler. Thread enter runnable state when start() executed or it’s coming back from blocked, waiting or sleeping state.
3.    Running - In this state the thread scheduler select the runnable thread from runnable pool to be the current executing process.
4.    Waiting/blocked/sleeping – In this state thread is not eligible to run and not in runnable state but if a particular condition or event occur it will come back to runnable state.to achieve this state we can call sleep() or yield() method.
5.    Dead – In this state thread execution is done and run() method completes. Once a thread is dead, it can never be brought back to life. When we call start() method on dead thread it will throw a runtime exception(java.lang.IllegalThreadStateException)

Thread Priorities and yield() 

Thread always run with some priority usually represented as a number between 1 to 10. The scheduler in most of the JVMs uses preemptive, priority based scheduling(which implies some sort of time slicing).
If a thread enters the runnable state and it has a higher priority than any of the thread in the pool and higher priority than the currently running thread. The lower priority running thread usually will be bumped back to runnable and highest priority thread will be chosen to run.
But don’t rely on thread priority when designing multithreaded application because thread scheduling priority behavior is not guaranteed, thread priority can be used to improve the efficiency of program, but not surety.
Yield() – to promote graceful turn taking among equal priority thread, but no guarantee.

The join() method – it make sure that 2nd thread will not start until tha first thread has finished.
Code Snippet –

/**
 * Creating thread via extending java.lang.Runnable Inteface
 * Using join() method threadTwo will start when threadOne completed
 * @author MANOJ
 */
public class TestRunnable implements Runnable {
     public void run() {
           System.out.println(Thread.currentThread()+" is running...");
     }

     public static void main(String args[]) {
           TestRunnable testRunnable = new TestRunnable();
           Thread threadOne = new Thread(testRunnable, "threadOne");
           Thread threadTwo = new Thread(testRunnable, "threadTwo");
           threadOne.start();
           try {
                threadOne.join();
           } catch (InterruptedException e) {
                e.printStackTrace();
           }
           System.out.println("Thread two start to run");
           threadTwo.start();
     }
}

Inter thread Interaction –

Inter thread communication is mechanism in which a thread communicate with other thread and release the resources as per the thread requirement.
For thread communication we use three methods define in object class.
wait () – It cause current threat to releases the lock and wait for specific time period or until notify() or notifyAll is called. Current thread must own this object so it must be called form synchronized context.
notify () – It will wakes up a single thread that is waiting on this object’s monitor.
notifyAll () – It will wakes up all thread that are waiting on this object’s monitor.

Code snippet –

/**
 * Creating thread via extending java.lang.Runnable Inteface
 * Using wait() and notify() method for inter-thread communication
 * these method must call from synchronized context otherwise will
 * throw runtime exception java.lang.IllegalMonitorStateException
 * @author MANOJ
 */
public class TestRunnable implements Runnable {
     public void run() {
           synchronized(this) {
                System.out.println(Thread.currentThread()+" is running...");
                notify();
           }
     }

     public static void main(String args[]) {
           TestRunnable testRunnable = new TestRunnable();
           Thread threadOne = new Thread(testRunnable, "threadOne");
           threadOne.start();
           synchronized (threadOne) {
                try {
                     threadOne.wait(1000);
                } catch (InterruptedException e) {
                     e.printStackTrace();
                }
           }
     }
}

Thread communication using notifyAll()

/**
 * In below code snippet three calculator thread are waiting for calculate
 * calculte is notifying all the waiting thread via notifyAll() method
 * @author Manoj
 */
public class TestCalculator {

     public static void main(String[] args) {
           Calculate calculate = new Calculate();
           Thread calcThread = new Thread(calculate);
           Thread thread1 = new Thread(new Calculator(calculate), "A");
           Thread thread2 = new Thread(new Calculator(calculate), "B");
           Thread thread3 = new Thread(new Calculator(calculate), "C");
           thread1.start();
           thread2.start();
           thread3.start();
           calcThread.start();
     }
}

class Calculator implements Runnable {
     private Calculate calculate;

     public Calculator(Calculate calculate) {
           this.calculate = calculate;
     }

     public void run() {
           synchronized (calculate) {
                try {
                     System.out.println("Waiting for calculation");
                     calculate.wait();
                } catch (InterruptedException e) {
                     e.printStackTrace();
                }
                System.out.println(calculate.sum);
           }
     }

}

class Calculate implements Runnable {
     int sum = 0;

     public void run() {
           for (int count = 0; count <= 10; count++) {
                sum = sum + count;
           }
           synchronized (this) {
                notifyAll();
           }
     }
}

Thread communication example in producer consumer problem

import java.util.List;
/**
 * Producer will put the value in taskQueue if queue is full it will wait for
 * consumer notification otherwise put the vale in queue and will call notify
 * @author Manoj
 */
public class Producer implements Runnable {

     private List<Integer> taskQueue;
     private int MAX_SIZE;

     Producer(List<Integer> sharedQueue, int size) {
           this.taskQueue = sharedQueue;
           MAX_SIZE = size;
     }

     public void run() {
           int counter = 0;
           while (true) {
                try {
                     produce(counter++);
                } catch (InterruptedException e) {
                     e.printStackTrace();
                }
           }
     }

     private void produce(int taskCounter) throws InterruptedException {
           synchronized (taskQueue) {
                while (taskQueue.size() == MAX_SIZE) {
                     System.out.println("Queue is full " + Thread.currentThread().getName());
                     taskQueue.wait();
                }
                Thread.sleep(1000);
                taskQueue.add(taskCounter);
                System.out.println("Produced: " + taskCounter);
                taskQueue.notify();
           }

     }
 }

-------------------------------------------------------------------------------------------------------------------------------
import java.util.List;
/**
 * Consumer will consume the value from taskQueue if queue is empty it will wait for           * producer notification otherwise consume the vale in queue and will call notify
 * @author Manoj
 */
public class Consumer implements Runnable {
     private List<Integer> taskQueue;

     Consumer(List<Integer> sharedQueue) {
           this.taskQueue = sharedQueue;
     }

     public void run() {
           while (true) {
                try {
                     consume();
                } catch (InterruptedException e) {
                     e.printStackTrace();
                }
           }
     }

     private void consume() throws InterruptedException {
           synchronized (taskQueue) {
                while (taskQueue.isEmpty()) {
                     System.out.println("Queue is empty " + Thread.currentThread().getName());
                     taskQueue.wait();
                }
                Thread.sleep(1000);
                System.out.println("Consumed: " + taskQueue.remove(0));
                taskQueue.notify();
           }
     }
}
-------------------------------------------------------------------------------------
public class TestProdCons {
     public static void main(String[] args) {
           List<Integer> taskQueue = new ArrayList<>();
           Thread prodThread = new Thread(new Producer(taskQueue, 5), "Producer");
           Thread consThread = new Thread(new Consumer(taskQueue), "Consumer");
           prodThread.start();
           consThread.start();
     }
}

Synchronization–

Synchronization provide mutual exclusion on shared resource. It provide locking and unlocking mechanism on shared object. Synchronized keyword can be used in code block or in a method.
1.      When a thread enters into java synchronized method or blocks it acquires a lock and when it leaves java synchronized method or block it releases the lock
2.      Java Thread acquires an object level lock when it enters into an instance synchronized java method and acquires a class level lock when it enters into static synchronized java method
3.      Java synchronized keyword is re-entrant in nature it means if a java synchronized method calls another synchronized method which requires the same lock then the current thread which is holding lock can enter into that method without acquiring the lock.
4.      You can’t use Java synchronized keyword with constructor it will give illegal modifier compiler error.

Code Snippet –
            Object1.method1() thread 1 executing synchronized method1()
            Object1.method2() thread 2 is waiting for
Synchronization is built around an internal entity known as the intrinsic lock or monitor lock.
So if a thread executing a synchronized block on an object than other thread can’t execute the any other synchronized block on that object but get lock from a different object.
If a thread executing static synchronized method than no other thread can get lock for any synchronized method of that class.


Volatile variable –

Volatile variable guarantees that value of the variable will always be read from main memory and not from thread local cache
Volatile variable reduces the risk of memory consistency.
Use of volatile variable in Singleton

public class Singleton {
private static volatile Singleton _instance;

public static Singleton getInstance() {
     if (_instance == null) {
           synchronized (Singleton.class) {
                if (_instance == null)
                     _instance = new Singleton();
           }
     }
     return _instance;
}
}

In above example if we don’t use volatile variable than memory inconsistency problem will occur
Suppose if a thread1 execute this class it will check if –instance is null it will acquire the lock and execute the synchronize block and at same time thread2 executed, but thread1 is not completed it just allocated the memory for _instance but object is not created completed in this situation thread2 will find that _instance is not null and it will get the partially created object.


6 comments:

  1. Double checking algorithm may still fail in creation of Singleton object, as the second thread may still see the _instance as null at line no. 5 & 7 and try creating second object.

    ReplyDelete
  2. Thanks mack,
    Second thread will not come inside synchronized block until lock releases by first thread. and on line no 5 it will find null and will wait for the lock, once the lock release by the first thread it will come inside the synchronized block and will check the null again on line 7 but till this point instance will be created by first thread, so it will not create the new instance.

    ReplyDelete
  3. Nice information, very usefull thanks

    ReplyDelete
  4. Good Questions with nice explanations

    ReplyDelete