Friday, 1 January 2021

Java Thread + MultiThreading + Concurrency

 


Thread -> A lightweight process.


Multithreading  ->When multiple threads execute byte-code instruction sequences in the same program, that action is known as multithreading.

[Example available in bottom section]


 There are two ways to create a thread.

 1) It can be created by extending the Thread class and overriding its run() method:

public class Main extends Thread {

  public void run() {

    System.out.println("This code is running in a thread");

  }

}

The thread can be run by creating an instance of the class and call its start() method

Main thread = new Main();

              thread.start();

 

2) Another way to create a thread is to implement the Runnable interface

public class Main implements Runnable {

  public void run() {

    System.out.println("This code is running in a thread");

  }

}

The thread can be run by passing an instance of the class to a Thread object's constructor and then calling the thread's start() method

Main obj = new Main();

              Thread thread = new Thread(obj);

              thread.start();

             

Differences between "extending" and "implementing" Threads

The major difference is that when a class extends the Thread class, you cannot extend any other class, but by implementing the Runnable interface, it is possible to extend from another class as well

 

Concurrency Problems: When the threads and main program are reading and writing the same variables, the values are unpredictable

Example:

public class Main extends Thread {

  public static int amount = 0;

 

  public static void main(String[] args) {

    Main thread = new Main();

    thread.start();

    System.out.println(amount);

    amount++;

    System.out.println(amount);

  }

 

  public void run() {

    amount++;

  }

}

 

Use isAlive() to prevent concurrency problems

public class Main extends Thread {

  public static int amount = 0;

 

  public static void main(String[] args) {

    Main thread = new Main();

    thread.start();

    // Wait for the thread to finish

    while(thread.isAlive()) {

    System.out.println("Waiting...");

  }

  // Update amount and print its value

  System.out.println("Main: " + amount);

  amount++;

  System.out.println("Main: " + amount);

  }

  public void run() {

    amount++;

  }

}



Threads exist in several states. Following are those states:  

  • New – When we create an instance of Thread class, a thread is in a new state.
  • Runnable – The Java thread is in running state.
  • Suspended – A running thread can be suspended, which temporarily suspends its activity. A suspended thread can then be resumed, allowing it to pick up where it left off.
  • Blocked – A java thread can be blocked when waiting for a resource.
  • Terminated – A thread can be terminated, which halts its execution immediately at any given time. Once a thread is terminated, it cannot be resumed. 


Thread class defines several methods that help manage threads.

Method Meaning
getNameObtain thread’s name
getPriorityObtain thread’s priority
isAliveDetermine if a thread is still running
joinWait for a thread to terminate
[Example available in bottom section]
runEntry point for the thread
sleepSuspend a thread for a period of time
startStart a thread by calling its run method


Synchronization:
When multiple threads try to access the same resource and finally they can produce unforeseen result due to concurrency issues
Java provides way of creating threads and synchronizing their task by using synchronized blocks You keep shared resources within this block
    synchronized(objectidentifier) {
       // Access shared variables and other shared resources
    }
[Example available in bottom section]



LOCK
A lock is a thread synchronization mechanism like synchronized blocks.
[Example available in bottom section]



DeadLockDeadlock describes a situation where two or more threads are blocked forever, waiting for each other.
[Example available in bottom section]



Wait and Notify:
Object.wait() – to suspend a thread
Object.notify() – to wake a thread up

wait() method causes the current thread to wait indefinitely until another thread either invokes notify() for this object or notifyAll()
wait(long timeout) method, we can specify a timeout after which thread will be woken up automatically. A thread can be woken up before reaching the timeout using notify() or notifyAll().
wait(0) is the same as calling wait()

notify() wakes up a single random thread
notifyAll() method simply wakes all threads that are waiting on this object's monitor.
[Example available in bottom section]


.................................................BOTTOM SECTION......................................................................

Join Example:
    thread1.start();
        thread1.join(1000);
    thread2.start();
Thread 2 waits for the completion of Thread 1 for 1000 milliseconds
The join() method can also be called without an argument. It this case, it simply waits until the thread dies


Example of Multi Threading:
class MyThread implements Runnable {
String name;
Thread t;
    MyThread (String thread){
    name = threadname; 
    t = new Thread(this, name);
System.out.println("New thread: " + t);
t.start();
}
 
 
public void run() {
 try {
     for(int i = 5; i > 0; i--) {
     System.out.println(name + ": " + i);
      Thread.sleep(1000);
}
}catch (InterruptedException e) {
     System.out.println(name + "Interrupted");
}
     System.out.println(name + " exiting.");
}
}
 
class MultiThread {
public static void main(String args[]) {
     new MyThread("One");
     new MyThread("Two");
     new NewThread("Three");
try {
     Thread.sleep(10000);
} catch (InterruptedException e) {
      System.out.println("Main thread Interrupted");
}
      System.out.println("Main thread exiting.");
      }
}

The output from this program is shown here:

New thread: Thread[One,5,main]
 New thread: Thread[Two,5,main]
 New thread: Thread[Three,5,main]
 One: 5
 Two: 5
 Three: 5
 One: 4
 Two: 4
 Three: 4
 One: 3
 Three: 3
 Two: 3
 One: 2
 Three: 2
 Two: 2
 One: 1
 Three: 1
 Two: 1
 One exiting.
 Two exiting.
 Three exiting.
 Main thread exiting


Example of Synchronization:
class PrintDemo {
   public void printCount() {
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Counter   ---   "  + i );
         }
      } catch (Exception e) {
         System.out.println("Thread  interrupted.");
      }
   }
}

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   PrintDemo  PD;

   ThreadDemo( String name,  PrintDemo pd) {
      threadName = name;
      PD = pd;
   }
   
   public void run() {
      synchronized(PD) {
         PD.printCount();
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }

   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      PrintDemo PD = new PrintDemo();

      ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );
      ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );

      T1.start();
      T2.start();

      // wait for threads to end
      try {
         T1.join();
         T2.join();
      } catch ( Exception e) {
         System.out.println("Interrupted");
      }
   }
}

Output
Starting Thread - 1
Starting Thread - 2
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 1  exiting.
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 2  exiting.


Example for Lock:
Instead of using:
    public int inc(){
        synchronized(this){
          return ++count;
        }
      }

  Locks can be used as below:

  private Lock lock = new Lock();
  private int count = 0;
  public int inc(){
    lock.lock();
    int newCount = ++count;
    lock.unlock();
    return newCount;
  }
  
  public class Lock{
  private boolean isLocked = false;

  public synchronized void lock()
  throws InterruptedException{
    while(isLocked){
      wait();
    }
    isLocked = true;
  }

  public synchronized void unlock(){
    isLocked = false;
    notify();
  }
}

Example for Deadlock:
public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();
   
   public static void main(String args[]) {
      ThreadDemo1 T1 = new ThreadDemo1();
      ThreadDemo2 T2 = new ThreadDemo2();
      T1.start();
      T2.start();
   }
   
   private static class ThreadDemo1 extends Thread {
      public void run() {
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");
            
            try { Thread.sleep(10); }
            catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");
            
            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }
   private static class ThreadDemo2 extends Thread {
      public void run() {
         synchronized (Lock2) {
            System.out.println("Thread 2: Holding lock 2...");
            
            try { Thread.sleep(10); }
            catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 1...");
            
            synchronized (Lock1) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}
Output:
Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...
Thread 2: Waiting for lock 1...
*The above program will hang forever

Deadlock Solution
In both threads make parent synchronized lock as lock1
In both threads make child synchronized lock as lock2


Example for Wait and Notify:
Sender-Receiver Synchronization Problem
1) The Sender is supposed to send a data packet to the Receiver
2) The Receiver cannot process the data packet until the Sender is finished sending it
3) Similarly, the Sender mustn't attempt to send another packet unless the Receiver has already processed the previous packet

Let's first create Data class that consists of the data packet that will be sent from Sender to Receiver. We'll use wait() and notifyAll() to set up synchronization between them:
public class Data {
    private String packet;
    
    // True if receiver should wait
    // False if sender should wait
    private boolean transfer = true;
 
    public synchronized void send(String packet) {
        while (!transfer) {
            try { 
                wait();
            } catch (InterruptedException e)  {
                Thread.currentThread().interrupt(); 
                Log.error("Thread interrupted", e); 
            }
        }
        transfer = false;
        
        this.packet = packet;
        notifyAll();
    }
 
    public synchronized String receive() {
        while (transfer) {
            try {
                wait();
            } catch (InterruptedException e)  {
                Thread.currentThread().interrupt(); 
                Log.error("Thread interrupted", e); 
            }
        }
        transfer = true;

        notifyAll();
        return packet;
    }
}
public class Sender implements Runnable {
    private Data data;
 
    // standard constructors
 
    public void run() {
        String packets[] = {
          "First packet",
          "Second packet",
          "Third packet",
          "Fourth packet",
          "End"
        };
 
        for (String packet : packets) {
            data.send(packet);

            // Thread.sleep() to mimic heavy server-side processing
            try {
                Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 5000));
            } catch (InterruptedException e)  {
                Thread.currentThread().interrupt(); 
                Log.error("Thread interrupted", e); 
            }
        }
    }
} 

public class Receiver implements Runnable {
    private Data load;
 
    // standard constructors
 
    public void run() {
        for(String receivedMessage = load.receive();
          !"End".equals(receivedMessage);
          receivedMessage = load.receive()) {
            
            System.out.println(receivedMessage);

            // ...
            try {
                Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 5000));
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); 
                Log.error("Thread interrupted", e); 
            }
        }
    }
}
public static void main(String[] args) {
    Data data = new Data();
    Thread sender = new Thread(new Sender(data));
    Thread receiver = new Thread(new Receiver(data));
    
    sender.start();
    receiver.start();
}

We'll receive the following output:
First packet
Second packet
Third packet
Fourth packet


No comments:

Post a Comment