Java Multithreading & Concurrency: A Complete Guide

Java Multithreading & Concurrency: A Complete Guide


🧭 Table of Contents

  1. What is Threading
  2. Thread Life Cycle
  3. How to Achieve Thread
  4. Join Method and Priority Thread
  5. Synchronization
  6. Dead Lock
  7. Daemon Thread
  8. Interrupted Thread
  9. Shutdown Hook
  10. Task Scheduling
  11. yield()
  12. Reentrant Lock
  13. CountDownLatch
  14. Cyclic Barrier
  15. Semaphore
  16. Thread Pools
  17. Blocking Queue
  18. Callable Interface
  19. Concurrent Linked Queue
  20. Exchanger
  21. Thread Factory
  22. Thread Local


1. What is Threading

Threading is used to achieve Multitasking in a java program that means single Programme or application of java can perform so many task simultaneously. Running state of instruction is known as process and static state of instruction is known as programme. Those instructions which are not taking separate memory area or address space in RAM to run itself are forming a light weight process.

Those instruction which are taking separate memory area or address space in a RAM to run itself are forming a heavy weight process. Thread are light weight process executing on a separate path and used to achieve multitasking in an application. One object of thread class is responsible to start one thread at a time. without creating a thread class object you can't start a thread.

JVM runs our program on particular thread called "Main Thread" which having a main() method. If nothing is left to executed within run() method then thread is dead automatically. Always make a separate class to start a main thread. If you want to perform a different different task on a different thread then make a separate thread class for each row. If you want to perform a common task on each thread then make a one thread class and create multiple object of that thread class.


2. Thread life Cycle

Article content
Thread life Cycle

3. How to Achieve Thread

Article content
Achieving of Thread

Thread can be achieved by two method by inheritance and by Association.

By Inheritance


class RunThread3 {
    public static void main (String... s){
        Thread1 t1 = new Thread1("thread1");
        Thread2 t2 = new Thread2("thread2");
        Thread3 t3 = new Thread3("thread3");
        
        t1.start();
        t2.start();
        t3.start();
        
        for (int i=1; i<=10; i++) {
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                
            }
        }
        System.out.println(Thread.currentThread().getName() + " dead");
    }
}


class Thread1 extends Thread {
    Thread1(String s) {
        super(s);
    }
    public void run() {
        for (int i=1; i<=3; i++){
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch(Exception e) {
                
            }
        }
        System.out.println(getName() + " dead");
    }
}

class Thread2 extends Thread {
    Thread2 (String s) {
        super(s);
    }
    public void run() {
        for (int i=1; i<=5; i++) {
            System.out.println(Thread.currentThread().getName());
            try  {
                Thread.sleep(1000);
            } catch (Exception e) {
                
            }
        }
        System.out.println(getName() + " dead");
    }
}

class Thread3 extends Thread {
    Thread3 (String s) {
        super(s);
    }
    
    public void run() {
        for (int i=1; i<=8; i++) {
            System.out.println(Thread.currentThread().getName());
            
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                
            }
        }
        System.out.println(getName() + " dead");
    }
}        

output:

thread2
thread3
main
thread1
thread2
thread3
thread1
main
thread2
thread3
thread1
main
thread2
thread3
main
thread1 dead
thread2
thread3
main
thread3
thread2 dead
main
thread3
main
thread3
main
main
thread3 dead
main
main dead        

By Association

class Thread3 implements Runnable {
    int x;
    public void run() {
        for(int i=1; i<= 5; i++) {
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                
            }
        }
    }
}


class RunThread3ByAssociation {
    public static void main (String... str){
        Thread3 t1 = new Thread3();
        t1.x=50;
        Thread tt1 = new Thread(t1, "Thread1");
        tt1.start();
        
        Thread3 t2 = new Thread3();
        t2.x=100;
        Thread tt2 = new Thread(t1, "Thread2");
        tt2.start();
        
        Thread3 t3 = new Thread3();
        t3.x=150;
        Thread tt3 = new Thread(t1, "Thread3");
        tt3.start();
        
        for (int i=1; i<=5; i++) {
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (Exception e ){
                
            }
        }
        
    }
}        

output:

Thread1
Thread3
Thread2
main
Thread3
Thread1
Thread2
main
Thread3
Thread1
Thread2
main
Thread3
Thread1
Thread2
main
Thread3
Thread1
main
Thread2        

Q: What happen if we call the run() method explicitly instead of start() method in Thread?

Ans: If we call the run() method explicitly, it behaves like a normal method call and does not start a new Thread. It executes in the current thread, ususally the main thread, and therefore no multithreading happens. Only the start() method actually created a new thread and internally calls run() on that new thread.


4. Join Method

Join method from Thread class is an important method and used to impose order on execution of multiple Threads. Concept of joining multiple threads is very popular on multithreading do you ensure that they finish in order T1, T2, T3. This question illustrate power of join method on multithreading programming. You can do this by using join method by calling T1.join() from T2 and T2.join() from T3. In this case Thread t1 will finish first followed by T2 and T3.

Join is a final method in java.lang.ThreadClass and you can't override it. Join ,method throws InterupptedException if another thread interrupted waiting thread as a result of join() call. Join is also an overloaded method in java join (long mili sec).


class JoinThread {
    public static void main(String... s){
        Thread t1 = new Thread1("Thread1");
        Thread t2 = new Thread2("Thread2");
        Thread t3 = new Thread3("Thread3");
        t1.start();
        t2.start();
        t3.start();
        
        try {
            t1.join();
            t2.join();
            t3.join();
        } catch (Exception e) {
            
        }
        
        for (int i=1; i<=7; i++) {
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                
            }
        }
        System.out.println(Thread.currentThread().getName());
    }
}


class Thread1 extends Thread {
    Thread1 (String s) {
        super(s);
    }
    public void run() {
        for (int i=1; i<=3; i++) {
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch(Exception e) {
                
            }
        }
        System.out.println(getName() + " dead");
    }
}

class Thread2 extends Thread {
    Thread2 (String s) {
        super(s);
    }
    public void run() {
        for (int i=1; i<=7; i++) {
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch(Exception e) {
                
            }
        }
        System.out.println(getName() + " dead");
    }
}

class Thread3 extends Thread {
    Thread3 (String s) {
        super(s);
    }
    public void run() {
        for (int i=1; i<=10; i++) {
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch(Exception e) {
                
            }
        }
        System.out.println(getName() + " dead");
    }
}        

Output:

Thread1
Thread2
Thread3
Thread2
Thread1
Thread3
Thread2
Thread1
Thread3
Thread2
Thread3
Thread1 dead
Thread2
Thread3
Thread2
Thread3
Thread2
Thread3
Thread3
Thread2 dead
Thread3
Thread3
Thread3 dead
main
main
main
main
main
main
main
main        

Priority Thread

If you call the run() method explicitly then no new thread is going to be started. In java always the priority thread will be highest in running state. No thread can be made dead forcely in java. By default the priority of parent thread is transfered to the child thread.

  • setPriority (int Priority)
  • setPriority (Thread.MIN_PRIORITY) = 1
  • setPriority (Thread.NORM_PRIORITY) = 5 (default)
  • setPriority (Thread.MAX_PRIORITY) = 10


public class PriorityThread {
    public static void main(String[] args) {
        SimpleThread t1 = new SimpleThread();
        SimpleThread t2 = new SimpleThread();

        t1.setName("HighPriorityThread");
        t2.setName("LowPriorityThread");

        t1.setPriority(Thread.MAX_PRIORITY); 
        t2.setPriority(Thread.MIN_PRIORITY); 

        t1.start();
        t2.start();
    }
}

class SimpleThread extends Thread {
    public void run() {
        System.out.println(getName() + " is running with priority " + getPriority());
    }
}        

Output:

LowPriorityThread is running with priority 1
HighPriorityThread is running with priority 10        

5. Synchronization

Whenever we want to share a common object among the multiple threads then we need to synchronize them. If any thread wants to call any synchronized method on a particular object then it must be having a lock of that object.Synchronization degrades the performance of a thread.

At a time only a one thread can enter into the synchronized method if all the threads are having same object. If a class is synchronized or threads of that means at a time only one thread can access the object of that class if all the threads are having a same object of that class. If you want to make your own class synchronized then make all the methods of that class synchronized.

StringBuffer is a synchronized class. StringBuilder is not a synchronized class. In java every class also maintain one implicit lock on it. which is known as Monitor and in case of static synchronized method lock is checked on class. In case of static synchronized method lock is always checked on a class hardly is always checked on a class hardly matters whether it is called by the class name or by the object. In java every object maintains one implicit lock on it that is known as monitor.

Difference between Synchronized method and Synchronized block

  1. In case of Synchronized method we make the whole method synchronized but in case of synchronized block we make particular portion of a method synchronized rather than whole method.
  2. In case of synchronized method lock can be achieved only on current object but in case of synchronized block lock can be achieved on any object.
  3. via synchronized block we can make the object of any class Synchronized.
  4. whenever we call suspend() method from the synchronized method or synchronized block then one dead lock is created.
  5. Suspend() method releases only the processor cycle but wait() method releases both processor cycle and object lock from the thread.

class RunSync {
    public static void main(String... str){
        Shared st = new Shared();
        CustomThread t1 = new CustomThread(st, "one");
        CustomThread1 t2 = new CustomThread1(st, "two");
	CustomThread2 t3 = new CustomThread2(st, "Three");
    }
}

class CustomThread extends Thread {
    Shared s;

    public CustomThread(Shared s, String str) {
        super(str);
        this.s = s;
        start();          
    }


    public void run() {
        s.show2(Thread.currentThread().getName(), 10);
    }
}

class CustomThread1 extends Thread {
    Shared s;

    public CustomThread1(Shared s, String str) {
        super(str);
        this.s = s;     
        start();
    }


    public void run() {
        s.show2(Thread.currentThread().getName(), 20);
    }
}

class CustomThread2 extends Thread {
    Shared s;

    public CustomThread2(Shared s, String str) {
        super(str);
        this.s = s;
        start();          
    }


    public void run() {
        s.show2(Thread.currentThread().getName(), 30);
    }
}

class Shared {
	int x;
    void show2(String s, int a){
        System.out.println("Starting in Method " + s );
        synchronized (this) {
            x=a;
            System.out.println("Starting in block " + s +" " + x);
            try {
                Thread.sleep(2000);
            } catch (Exception e){
                
            }
            System.out.println("Exit from block " + s + " " + x);
        }
    }
}        

Output:

Starting in Method two
Starting in Method one
Starting in Method Three
Starting in block two 20
Exit from block two 20
Starting in block Three 30
Exit from block Three 30
Starting in block one 10
Exit from block one 10        

Condition -1

Article content
Condition-1

If two different thread call two different synchronized method on the same object they can't execute them simultaneously because all synchronized instance methods use the same object- level lock, so one thread must finish its synchronized method before another thread can enter any another synchonized method of that same object.

class RunSync {
    public static void main(String... str){
        Shared st = new Shared();
        CustomThread t1 = new CustomThread(st, "one");
        CustomThread1 t2 = new CustomThread1(st, "two");
    }
}

class CustomThread extends Thread {
    Shared s;

    public CustomThread(Shared s, String str) {
        super(str);
        this.s = s;
        start();          }


    public void run() {
        s.show(Thread.currentThread().getName(), 10);
    }
}

class CustomThread1 extends Thread {
    Shared s;

    public CustomThread1(Shared s, String str) {
        super(str);
        this.s = s;     
        start();
    }


    public void run() {
        s.show1(Thread.currentThread().getName(), 20);
    }
}

class Shared {
    int x , y;

    synchronized void show(String s, int a)    {
        x=a;
        System.out.println("Starting in method " + s + " " + x);
        try {
            Thread.sleep(2000);
        } catch (Exception e){
            
        }
        System.out.println("Exit from method " + s + " " + x);
    }
    
    
    synchronized void show1 (String s, int a){
	x=a;
        System.out.println("Starting show1 " + s + " " + x);
        try {
            Thread.sleep(2000);
        }catch (Exception e){
            
        }
        System.out.println("Ending Show1 " + s + " " + x);
    }
        
    
}        

Output:

Starting in method one 10
Exit from method one 10
Starting show1 two 20
Ending Show1 two 20        

Condition -2

Article content
Condition-2

If one thread enters a synchronized method another thread can still execute any non-synchronized method on the same object at the same time because non-synchronized methods do not require the object lock.

class RunSync {
    public static void main(String... str){
        Shared st = new Shared();
        CustomThread t1 = new CustomThread(st, "one");
        CustomThread1 t2 = new CustomThread1(st, "two");
    }
}

class CustomThread extends Thread {
    Shared s;

    public CustomThread(Shared s, String str) {
        super(str);
        this.s = s;
        start();          
    }


    public void run() {
        s.show(Thread.currentThread().getName(), 10);
    }
}

class CustomThread1 extends Thread {
    Shared s;

    public CustomThread1(Shared s, String str) {
        super(str);
        this.s = s;     
        start();
    }


    public void run() {
        s.show1(Thread.currentThread().getName(), 20);
    }
}

class Shared {
    int x , y;

    synchronized void show(String s, int a)    {
        x=a;
        System.out.println("Starting in method " + s + " " + x);
        try {
            Thread.sleep(1000);
        } catch (Exception e){
            
        }
        System.out.println("Exit from method " + s + " " + x);
    }
    
    
    void show1 (String s, int a){
        x=a;
        System.out.println("Starting show1 " + s);
        try {
            Thread.sleep(1000);
        }catch (Exception e){
            
        }
        System.out.println("Ending Show1 " + s);
    }
        
}        

Output:

Starting show1 two
Starting in method one 10
Ending Show1 two
Exit from method one 20        

Condition -3

Article content
Condition-3

If three different threads call a static synchronized method, then all of them compete for the class-level lock (Class object lock). Only one thread can execute any static synchronized method of that class at a time. The another two thread must wait in block pool util the lock is released.

class RunSync {
    public static void main(String... str){
        Shared st = new Shared();
        CustomThread t1 = new CustomThread(st, "one");
        CustomThread1 t2 = new CustomThread1(st, "two");
        CustomThread2 t3 = new CustomThread2(st, "three");
    }
}

class CustomThread extends Thread {
    Shared s;

    public CustomThread(Shared s, String str) {
        super(str);
        this.s = s;
        start();          
    }


    public void run() {
        Shared.show(Thread.currentThread().getName(), 10);
    }
}

class CustomThread1 extends Thread {
    Shared s;

    public CustomThread1(Shared s, String str) {
        super(str);
        this.s = s;     
        start();
    }


    public void run() {
        Shared.show(Thread.currentThread().getName(), 20);
    }
}


class CustomThread2 extends Thread {
    Shared s;

    public CustomThread2(Shared s, String str) {
        super(str);
        this.s = s;     
        start();
    }


    public void run() {
        Shared.show(Thread.currentThread().getName(), 30);
    }
}

class Shared {
    static int x , y;

    static synchronized void show(String s, int a)    {
        x=a;
        System.out.println("Starting in method " + s + " " + x);
        try {
            Thread.sleep(1000);
        } catch (Exception e){
            
        }
        System.out.println("Exit from method " + s + " " + x);
    }
}        

Output:

Starting in method one 10
Exit from method one 10
Starting in method three 30
Exit from method three 30
Starting in method two 20
Exit from method two 20        

Condition -4

Article content
condition-4

If one thread calls a static synchronized method using an object and another thread calls the same method using the class name, both threads will be synchronized. This is because static synchronized method lock on the class object, not on any instance. Therefore both threads will comete for the same class-level lock and only one thread can execute the method at a time.

class RunSync {
    public static void main(String... str){
        Shared st = new Shared();
        CustomThread t1 = new CustomThread(st, "one");
        CustomThread1 t2 = new CustomThread1(st, "two");
    }
}

class CustomThread extends Thread {
    Shared s;

    public CustomThread(Shared s, String str) {
        super(str);
        this.s = s;
        start();          
    }


    public void run() {
        Shared.show(Thread.currentThread().getName(), 10);
    }
}

class CustomThread1 extends Thread {
    Shared s;

    public CustomThread1(Shared s, String str) {
        super(str);
        this.s = s;     
        start();
    }


    public void run() {
        s.show(Thread.currentThread().getName(), 20);
    }
}

class Shared {
    static int x , y;

    static synchronized void show(String s, int a)    {
        x=a;
        System.out.println("Starting in method " + s + " " + x);
        try {
            Thread.sleep(1000);
        } catch (Exception e){
            
        }
        System.out.println("Exit from method " + s + " " + x);
    }
}        

Output:

Starting in method one 10
Exit from method one 10
Starting in method two 20
Exit from method two 20        

Condition -5

Article content
condition-5

If one thread calls a static synchronized method and another threads call a synchronized instance method, both methods can run at the same time without blocking each other.

class RunSync {
    public static void main(String... str){
        Shared st = new Shared();
        CustomThread t1 = new CustomThread(st, "one");
        CustomThread1 t2 = new CustomThread1(st, "two");
    }
}

class CustomThread extends Thread {
    Shared s;

    public CustomThread(Shared s, String str) {
        super(str);
        this.s = s;
        start();          
    }


    public void run() {
        Shared.show(Thread.currentThread().getName(), 10);
    }
}

class CustomThread1 extends Thread {
    Shared s;

    public CustomThread1(Shared s, String str) {
        super(str);
        this.s = s;     
        start();
    }


    public void run() {
        s.show1(Thread.currentThread().getName(), 20);
    }
}


class Shared {
    static int x , y;

    static synchronized void show(String s, int a)    {
        x=a;
        System.out.println("Starting in method " + s + " " + x);
        try {
            Thread.sleep(1000);
        } catch (Exception e){
            
        }
        System.out.println("Exit from method " + s + " " + x);
    }


    synchronized void show1 (String s, int a){
        System.out.println("Starting show1 " + s);
        try {
            Thread.sleep(2000);
        }catch (Exception e){
            
        }
        System.out.println("Ending Show1 " + s);
    }
}        

Output:

Starting show1 two
Starting in method one 10
Exit from method one 10
Ending Show1 two        

6. Dead Lock

A deadlock is a situation where a tread is waiting for one object lock that another thread holds.

Article content
Deadlock: circular wait between two processes

Since each thread is waiting for the other thread to release a lock they both remain waiting forever in the blocked pool for lock acquisition state. The Thread are said to be deadlock.

public class DeadlockExample {

    private static final Object resource1 = new Object();
    private static final Object resource2 = new Object();

    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("Thread 1: Locked Resource 1");

                try { Thread.sleep(100); } catch (InterruptedException e) {}

                System.out.println("Thread 1: Waiting for Resource 2");
                synchronized (resource2) {
                    System.out.println("Thread 1: Locked Resource 2");
                }
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("Thread 2: Locked Resource 2");

                try { Thread.sleep(100); } catch (InterruptedException e) {}

                System.out.println("Thread 2: Waiting for Resource 1");
                synchronized (resource1) {
                    System.out.println("Thread 2: Locked Resource 1");
                }
            }
        });

        t1.start();
        t2.start();
    }
}        

Output:

Thread 1: Locked Resource 1
Thread 2: Locked Resource 2
Thread 1: Waiting for Resource 2
Thread 2: Waiting for Resource 1
|        

7. Daemon Thread

Daemon thread are service provider thread. They provide service to another thread. They always run in background. They never show any output. They will be dead automaticaaly if a thread to whome they were providing a service is dead. Garbage collector thread is a daemono thread.



public class DaemonExample {
    public static void main(String[] args) {

        MyDaemonThread t = new MyDaemonThread();

        t.setDaemon(true);

        t.start();

        for (int i = 1; i <= 5; i++) {
            System.out.println("Main thread running: " + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {}
        }

        System.out.println("Main thread finished");
    }
}

class MyDaemonThread extends Thread {
    public void run() {
        while (true) {
            System.out.println("Daemon thread is running...");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                System.out.println(e);
            }
        }
    }
}        

Output:

Daemon thread is running...
Main thread running: 1
Daemon thread is running...
Main thread running: 2
Daemon thread is running...
Daemon thread is running...
Main thread running: 3
Daemon thread is running...
Daemon thread is running...
Main thread running: 4
Daemon thread is running...
Daemon thread is running...
Main thread running: 5
Daemon thread is running...
Daemon thread is running...
Main thread finished        

8. Interrupted Thread

A Thread Terminates when it's run() method return. In jdk 1.0 there was a stop() method that another thread could call to terminate a thread. However that method is now removed. There is no longer a way to force a thread to terminate a thread. However the interrupt method can be used to request termination of thread. When the interrupt() method is called on the thread the interrupt status of the thread is set. This is boolean flag that is present in every thread. Each thread should occasionally check whether it is interrupted.

However if a thread is blocked it can't check the interrupted status. This is where the interrupted exception comes in. Where the interrupt() method is called on a blocked thread the blocking call (Such as sleep or wait) is terminated by an Interrupted Exception. There is no language requirement that a thread is interrupted should terminate. Interrupting a thread simply grab it's attension.

class Interrupt {
    public static void main(String... s){
        Thread1 t1= new Thread1("Thread1");
        t1.setPriority(10);
        Thread2 t2 = new Thread2("Thread2",t1);
        t1.start();
        t2.start();
    }
}

class Thread1 extends Thread {
    Thread1 (String s) {
        super (s);
    }
    public void run() {
        System.out.println(getName());
        
        try {
            Thread.sleep(6000*10);
        } catch (Exception e){
            System.out.println("Interrupted Forcely:-  " + e);
        }
        System.out.println(getName() + " dead");
    }
}

class Thread2 extends Thread {
    Thread1 t;
    Thread2 (String s , Thread1 t){
        super(s);
        this.t = t;
    }
    public void run() {
        System.out.println(getName() );
        t.interrupt();
        try {
            Thread.sleep(1000);
        } catch (Exception e){
            
        }
        System.out.println(getName() + " dead");
    }
}        

Output:

Thread1
Thread2
Interrupted Forcely:-  java.lang.InterruptedException: sleep interrupted
Thread1 dead
Thread2 dead        

9. Shutdown Hook

shutdown hook is an initialized but an unstarted thread, which is invoked when the Java virtual-machine is shutdown. Java virtual-machine can shutdown in two ways first is the normal shutdown when the last non-daemonthread exits or when the Virtual Machine is exited using System.exit or Runtime.exit. Second way of Virtual Macine termination is the user interruption, like by pressing CTRL+C, or user Logs off or System shutdown.

The shutdown hook can be registered to the java.lang.Runtime class by using the addShutdown Hook (Thread hook) method. This method is only available in JDK above 1.2.2. All the registered shutdown hook will be started in specific order and will run concurrently when the virtual machine begins its shutdown sequence. Once the shutdown sequence is started. It is not possible to registere new hooks or previous registered hook.


import java.awt.*;
import javax.swing.*;

public class ShutdownHooks implements Runnable {
    public void run() {
        System.out.println("*** Application Shutting down ***");
        ShutdownHooks1 hook = new ShutdownHooks1();
        Thread t1 = new Thread(hook);
        t1.start();
        try {
            t1.join();
        } catch (Exception e) {
            
        }
        System.out.println("*** After Backup ***");
    }
}

class ShutdownHooks1 implements Runnable {
    public void run() {
        System.out.println("*** Taking Backup and Closing resources ***");
        try {
            Thread.sleep(1000*5);
        } catch (Exception e) {
            
        }
    }
}

class RunHookup {
    public static void main (String... str) {
        Runtime runtime = Runtime.getRuntime();
        ShutdownHooks hook = new ShutdownHooks ();
        
        runtime.addShutdownHook(new Thread(hook));
        // System.exit(0);
        // int x=10/0;
        JFrame testFrame = new JFrame (" Test Frame ");
        testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        testFrame.setSize(400,400);
        testFrame.setVisible(true);
        System.out.println("Hello");
    }
}        

10. Task Scheduling

Task scheduling is done using Timer and TimerTask to execute a task after a delay and repeatedly at a fixed interval without user interaction.

import java.util.*;
import javax.swing.JFrame;

class Task extends TimerTask {
    int count = 1;
    
    public void run() {
        JFrame f = new JFrame();
        f.setSize(400,400);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setVisible(true);
        count++;
    }
}

class TaskScheduling {
    public static void main (String... str) {
        Timer timer = new Timer();
        int delay = 5000;
        int period = 1000;
        timer.scheduleAtFixedRate( new Task(), delay, period);
    }
}        

11. yield()

yield() make the currently running thread head back to runnable to allow another threads to get their turn. A thread can be moved out of the virtual CPU by yielding. A thread that has yieldede goes into the ready state. The yield() method is a static method of the Thread class. It always causes the currently executing thread to yield.


public class YieldEx {
    public static void main(String... str) {
        MyThread ct = new MyThread();
        ct.start();
        try {
            Thread.sleep(1000);
        }catch (Exception e) {
            
        }
        Thread.yield();
        System.out.println("Main ");
    }
}

class MyThread extends Thread {
    public MyThread() {
        
    }
    
    public void run() {
        for (int i=1; i<=10; i++) {
            System.out.println("Counting: " + i);
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                
            }
        }
    }
}        

Output:

Counting: 1
Main 
Counting: 2
Counting: 3
Counting: 4
Counting: 5
Counting: 6
Counting: 7
Counting: 8
Counting: 9
Counting: 10        

12. Reentrant Lock

A reentrant Lock is a high-level synchronization tool in java that provides more flexibility and control than the standard synchronization. Unlike synchronized blocks, which automatically release the lock when the block ends but in Reentrant Lock must manually call lock() and unlck(). It maintaining a count of how many times a thread has acquired the lock. For ecvery lock() call, there must be a Corresponding unblock() call before the lock is fully released for another thread.

for example:- Hr of a company wants to hire 3 java developer. For this he select 3 Tech Leads to conduct test and select candidates. On Tech Lead will Select one candidate but their is only one question paper and xerox machine is not working so now at a time only one Tech can conduct the test. When one Tech Lead is having question paper it conducts the test and another. Tech LEads have to wait for question paper when one Tech LEad has taken the test then he given question paper back. Now other tech Leads can take the question paper and conduct test.

import java.util.concurrent.locks.ReentrantLock;

public class Main_Hr{
    public static void main(String... ss1){
        ReentrantLock question_paper = new ReentrantLock();
        
        new TechLead1 (question_paper, "Tushar").start();
        new TechLead2 (question_paper, "Shivam").start();
        new TechLead3 (question_paper, "Abhinav").start();
        System.out.println("Hr Completes his work");
    }
}

class TechLead1 extends Thread {
    ReentrantLock question_paper;
    String name;
    TechLead1 (ReentrantLock question_paper, String name){
        super(name);
        this.question_paper = question_paper;
    }
    
    public void run() {
        System.out.println(getName() + " is waiting for question papaer");
        question_paper.lock();
        System.out.println(getName() + " takes the question paper");
        System.out.println(getName() + " start taking test");
        try {
            Thread.sleep(1000);
        } catch (Exception e){
            System.out.println(getName() + " is interrupted");
        }
        
        System.out.println(getName() + " has taken the test");
        System.out.println(getName() + " return the question paper");
        question_paper.unlock();
    }
}

class TechLead2 extends Thread {
    ReentrantLock question_paper;
    String name;
    TechLead2(ReentrantLock question_paper, String name){
        super(name);
        this.question_paper = question_paper;
    }
    
    public void run() {
        System.out.println(getName() + " is waiting for question paper");
        question_paper.lock();
        System.out.println(getName() + " takes the question paper");
        System.out.println(getName() + " starts taking test");
        
        try {
            Thread.sleep(1000);
        } catch (Exception e){
            System.out.println(getName() + "is interrupted ");
        }
        
        System.out.println(getName() + " has taken the test");
        System.out.println(getName() + "returns the question paper");
        question_paper.unlock();
    }
}

class TechLead3 extends Thread {
    ReentrantLock question_paper;
    String name;
    TechLead3 (ReentrantLock question_paper, String name){
        super(name);
        this.question_paper = question_paper;
    }
    
    public void run() {
        System.out.println(getName() + " is waiting for question paper ");
        question_paper.lock();
        System.out.println(getName() + " takes the question paper");
        System.out.println(getName() + " returns the question paper");
        question_paper.unlock();
    }
}        

Output:

Hr Completes his work
Shivam is waiting for question paper
Abhinav is waiting for question paper 
Shivam takes the question paper
Shivam starts taking test
Tushar is waiting for question papaer
Shivam has taken the test
Shivamreturns the question paper
Abhinav takes the question paper
Abhinav returns the question paper
Tushar takes the question paper
Tushar start taking test
Tushar has taken the test
Tushar return the question paper        

13. CountDownLatch

A CountDownLatch is a high-level synchronization utility in java that allows one or more threads to wait until a set of operations being performed in other threads completes.

for example:- This is a more advanced type of synchronized that can be dome with concurrent package. Consider the example where a organization needs to recruite 3 Javaa Developers. For this HR Manager has asked 3 tech Leads to take interview. The HR Manager wants to distributed the offer letter only after all the 3 Java Developers have been recruited. In threading terminology the HR Manager should wait till 3 Java Developer have been recruited.

import java.util.concurrent.CountDownLatch;

public class HRManager {
    public static void main(String... ss1){
        CountDownLatch countDownLatch = new CountDownLatch(3);
        
        TechLead techLead1 = new TechLead(countDownLatch, "Tushar");
        TechLead techLead2 = new TechLead(countDownLatch, "Parth");
        TechLead techLead3 = new TechLead(countDownLatch, "Shivam");
        
        techLead1.start();
        techLead2.start();
        techLead3.start();
        
        try {
            System.out.println("HR Manager Waiting for recruitment to complete...  ");
            countDownLatch.await();
            System.out.println("Distribute offer Letter");
        } catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

class TechLead extends Thread {
    CountDownLatch countDownLatch;
    public TechLead(CountDownLatch countDownLatch, String name){
        super(name);
        this.countDownLatch = countDownLatch;
    }
    
    @Override
    public void run() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e){
            e.printStackTrace();
        }
        
        System.out.println(Thread.currentThread().getName() + " : recruted");
        
        countDownLatch.countDown();
        
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e){
            e.printStackTrace();
        }
        
        System.out.println(Thread.currentThread().getName() + ": Dead");
    }
}        

Output:

HR Manager Waiting for recruitment to complete...  
Tushar : recruted
Parth : recruted
Shivam : recruted
Distribute offer Letter
Tushar: Dead
Parth: Dead
Shivam: Dead        

14. Cyclic Barrier

CyclicBarrier a synchronized aid that allows a set of threads to all wait for each other ro reach a common barrier point. CyclicBarrier are useful in programs involving a fixed sized porty of threads that must occasionally wait for each other. The barrier is called cyclic because it can be re-used after the waiting threads are released.

CountDownBatch we saw how a master or main thread waits till worker threads finish their work CyclicBarrier class also is a flavour of countDownLatch with slight change.

for Example:- A organization has to recruits 3 Java developer and so the HR Manager asks 3 Tech Leads to interview the candidates. In CountDownLatch example the HR Manager wanted to distribute the offer Letter to all the 3 Candidates that is the reason we made himto wait. Here the HR Manager wants the Tech Leads to give the offer letter once they have selected the candidate but the TechLeads decide among themselves that they will give the offer letter to their respective candidate only when all interviews are done.

import java.util.concurrent.CyclicBarrier;

public class HRManager {
    public static void main(String... ss1){
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
        TechLead techLead1 = new TechLead(cyclicBarrier,"Tushar");
        TechLead techLead2 = new TechLead(cyclicBarrier,"Shivam");
        TechLead techLead3 = new TechLead(cyclicBarrier,"Anubhav");
        
        techLead1.start();
        techLead2.start();
        techLead3.start();
        
        System.out.println("No work for HR manager");
    }
}

class TechLead extends Thread {
    CyclicBarrier cyclicBarrier;
    
    public TechLead(CyclicBarrier cyclicBarrier, String name){
        super(name);
        this.cyclicBarrier = cyclicBarrier;
    }
    
    @Override 
     public void run() {
        try {
             Thread.sleep(3000);
             System.out.println(Thread.currentThread().getName() + " recruited developer");
             System.out.println(Thread.currentThread().getName() + " waiting for others to complete... ");
             cyclicBarrier.await();
             System.out.println("All finished recruiting " + Thread.currentThread().getName() + " gives offer letter to candidate");
         } catch (Exception e){
             e.printStackTrace();
         }
     }
}        

Output:

No work for HR manager
Anubhav recruited developer
Anubhav waiting for others to complete... 
Tushar recruited developer
Tushar waiting for others to complete... 
Shivam recruited developer
Shivam waiting for others to complete... 
All finished recruiting Tushar gives offer letter to candidate
All finished recruiting Shivam gives offer letter to candidate
All finished recruiting Anubhav gives offer letter to candidate        

15. Semaphore

Semaphore class in concurrent package is used as a pool that can be acuired and released very much like lock but with a difference when a thread acquired a lock not other thread can enter the synchronise block. Here in semaphore we can define the poolsize and threads can accuire till there is resource left in the pool.

for example:- The organization needs to recruits 4 Java developers HR Manager asks 4 Tech Leads to conduct test and recruits the candidates. The problem here is that the Tech Leads only 2 test paper and the photo copy machine is down. That means at a time only two candidates can give the test and other 2 have to wait.

import java.util.concurrent.Semaphore;

public class HRManager {
    public static void main(String... ss1) {
        Semaphore questionPaperPool = new Semaphore(2);
        
        TechLead techLead1 = new TechLead(questionPaperPool,"Tushar");
        TechLead techLead2 = new TechLead(questionPaperPool,"Parth");
        TechLead techLead3 = new TechLead(questionPaperPool,"Shivam");
        TechLead techLead4 = new TechLead(questionPaperPool,"Ashish");
        
        techLead1.start();
        techLead2.start();
        techLead3.start();
        techLead4.start();
        
        System.out.println("No work for HR manager");
    }
}

class TechLead extends Thread {
    Semaphore questionPaperPool;
    
    public TechLead(Semaphore questionPaperPool, String name) {
        super(name);
        this.questionPaperPool = questionPaperPool;
    }
    
    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName() + " waiting for test question paper");
            
            questionPaperPool.acquire();
            System.out.println(Thread.currentThread().getName() + " acquired test paper");
            System.out.println(Thread.currentThread().getName() + " Conducting Test");
            Thread.sleep(3000);
            System.out.println(Thread.currentThread().getName() + "Test done giving back the paper");
            
            questionPaperPool.release();
        } catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}        

Output:

No work for HR manager
Ashish waiting for test question paper
Shivam waiting for test question paper
Shivam acquired test paper
Tushar waiting for test question paper
Parth waiting for test question paper
Ashish acquired test paper
Ashish Conducting Test
Shivam Conducting Test
ShivamTest done giving back the paper
AshishTest done giving back the paper
Parth acquired test paper
Parth Conducting Test
Tushar acquired test paper
Tushar Conducting Test
ParthTest done giving back the paper
TusharTest done giving back the paper        

16. Thread Pools

Thread Pools are useful when you need to limit the number of threads running in your application at the same time. There is performance overhead associated with starting a new thread and each thread is also allocated some memory for its stack etc. Sever that processing requests can spend more time and Consume more system resources in creating and destroying threads than it would processing actual client requests.

Instead of starting a new thread for every task to execute concurrently, the task can be passed to a thread pool. As soon as the pool has any idle thread pool. As soon as the pool has any idle threads the task is assigned to one of them and executed. Internally the taks are inserted into the queue one of the idle threads will dequeue it successfully and execute it. The rest of the idle threads in the pool will be blocked waiting to dequeue tasks.

It is a pool of worker threads with life cycle as follows.

  1. Get a new task to execute
  2. Execute it
  3. Go back to waiting for next task.

Thread pools are often used in multi-threaded severs. Each connection arriving at the Server via the network is wrapper as a task and passed on to a thread pool. The threads in the thread pool will process the requests on the connections concurrently. Java 5 comes with built in thread pools in the java.util.concurrent package. So you don't have to implement your own thread pool.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class SimpleThreadPool {
    public static void main(String... ss1){
        ExecutorService executor = Executors.newFixedThreadPool(5);
        for (int i=0; i<10; i++) {
            Runnable worker = new WorkerThread(" " + i);
            executor.execute(worker);
        }
        
        executor.shutdown();
        while(!executor.isTerminated()){
            
        }
        System.out.println("Finished all threads");
    }
}

class WorkerThread implements Runnable {
    private String command;
    
    public WorkerThread(String s){
        this.command = s;
    }
    
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " Start command = " + command);
        processCommand();
        System.out.println(Thread.currentThread().getName() + " End ");
    }
    
    
    private void processCommand() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e){
            e.printStackTrace();
        }
    }
    
    public String toString() {
        return this.command;
    }
}        

Output:

pool-1-thread-3 Start command =  2
pool-1-thread-1 Start command =  0
pool-1-thread-2 Start command =  1
pool-1-thread-4 Start command =  3
pool-1-thread-5 Start command =  4
pool-1-thread-1 End 
pool-1-thread-3 End 
pool-1-thread-4 End 
pool-1-thread-4 Start command =  7
pool-1-thread-1 Start command =  5
pool-1-thread-3 Start command =  6
pool-1-thread-2 End 
pool-1-thread-2 Start command =  8
pool-1-thread-5 End 
pool-1-thread-5 Start command =  9
pool-1-thread-4 End 
pool-1-thread-1 End 
pool-1-thread-3 End 
pool-1-thread-2 End 
pool-1-thread-5 End 
Finished all threads        

17. BlockingQueue

A blocking Queue is a queue that blocking when you try to dequeue from it and the queue is empty or if you try to enqueue items to it and the queue is already full. A thread trying to dequeue from an empty queue is blocked until some other thread inserts an item into the queue. A thread trying to enqueue an item in a full queue is blocking util some other thread makes space in the queue, either by dequeuing one or more items or clearing the queue completely.

A blocking queue with one thread putting into it, and another thread taking from it. Java 5 comes with blocking queue implementations in the java.util.Concurrent.BlockingQueue package.

import java.util.concurrent.*;

public class BlockingQueueExample {
    public static void main(String... s) throws Exception {
        BlockingQueue queue= new ArrayBlockingQueue(5);
        
        Producer producer = new Producer(queue);
        Consumer consumer = new Consumer(queue);
        
        System.out.println("Starting Producer");
        new Thread(producer).start();
        System.out.println("Starting producer");
        new Thread(consumer).start();
    }
}

class Producer implements Runnable {
    protected BlockingQueue queue = null;
    public Producer(BlockingQueue queue) {
        this.queue = queue;
    }
    
    public void run() {
        try {
            queue.put("String ONE");
            Thread.sleep(3000);
            queue.put("String Two");
            Thread.sleep(3000);
            queue.put("String Three");
        } catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

class Consumer implements Runnable {
    protected BlockingQueue queue = null;
    
    public Consumer(BlockingQueue queue) {
        this.queue = queue;
    }
    
    public void run() {
        try {
            System.out.println(queue.take());
            System.out.println(queue.take());
            System.out.println(queue.take());
        } catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}        

Output:

Starting Producer
Starting producer
String ONE
String Two
String Three        

18. Callable Interface

callable interface use generic to define the return type of object executors class provide useful methods to execute callable in a thread pool. Since callable tasks run in parallel, We have to wait for the returned object. Callable tasks return java util concurrent future object. Using future we can find out the status of the callable task and get the returned object. It provides get() method that can wait for the callable to finish and then return the result.Future provides cancel() method to cancel the associated callable task. There is an overloaded version of get() method where we can specify the time to wait for the result. it's usefult to avoid current thread getting blocked for longer time(). There are isDone() and isCancelled() methods to find out the current status of associatd callable tasks. Here is a simple example of callable tasks that returns the name of thread executing the task after one second. We are using executor framework to execute 10 tasks in parallel and use future to get the result of the submitted tasks.

Once we execute the above program, you will notice the delay in output because future get() method waits for the callable task complete. When you submit a callable object to an executor, the framework returns an object of type java.util.Concurrent.Future. This Future object is used to check the results of a callable. Use the get() method to retreive the result of the future.

import java.util.concurrent.*;

class MyCallableTest {
    public static void main(String... s) {

        ExecutorService executor = Executors.newFixedThreadPool(5);

        Future<String>[] list = new Future[10];

        for (int i = 0; i < 10; i++) {
            list[i] = executor.submit(new MyCallable());
        }

        for (Future<String> future : list) {
            try {
                System.out.println(future.get());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        executor.shutdown();
    }
}

class MyCallable implements Callable<String> {
    public String call() throws Exception {
        System.out.println(Thread.currentThread().getName() + " start");
        Thread.sleep(2000);
        System.out.println(Thread.currentThread().getName() + " ends");
        return Thread.currentThread().getName();
    }
}         

Output:

pool-1-thread-3 start
pool-1-thread-5 start
pool-1-thread-2 start
pool-1-thread-4 start
pool-1-thread-1 start
pool-1-thread-2 ends
pool-1-thread-4 ends
pool-1-thread-3 ends
pool-1-thread-1 ends
pool-1-thread-2 start
pool-1-thread-1 start
pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
pool-1-thread-4
pool-1-thread-5 ends
pool-1-thread-5 start
pool-1-thread-4 start
pool-1-thread-3 start
pool-1-thread-5
pool-1-thread-2 ends
pool-1-thread-1 ends
pool-1-thread-5 ends
pool-1-thread-3 ends
pool-1-thread-4 ends
pool-1-thread-4
pool-1-thread-2
pool-1-thread-3
pool-1-thread-1
pool-1-thread-5        

19. Concurrent Linked Queue

Concurrent Linked Queue is an unbounded thread-safe queue based on linked nodes. This queue orders elements as a FIFO (First-in-first-out). The head of the queue is that elements that has been on the queue the longest time. The tail of the queue is that elements that has been on the queue the shortest time. New elements are inserted at the tail of the queue and the queue retrieval operations obtain elements at the head of the queue.

A Concurrent Linked Queue is a good choice when many threads share access to a common collection like most other concurrent Collection implementations, this class doesn't permit the use of null elements.

import java.util.concurrent.*;

public class ConcurrentLinkedQueueExample {
    public static void main(String... ss1){
        ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<String>();
        
        Thread producer = new Thread(new Producer(queue));
        Thread consumer = new Thread(new Consumer(queue));
        producer.start();
        consumer.start();
    }
}

class Producer implements Runnable {
    ConcurrentLinkedQueue<String> queue;
    Producer(ConcurrentLinkedQueue<String> queue) {
        this.queue =queue;
    }
    
    public void run() {
        System.out.println("Producer Started");
        try {
            for (int i=1; i<10; i++){
                queue.add("String" + i);
                System.out.println("Added: String" + i);
                Thread.currentThread().sleep(1000);
            }
        } catch (Exception ex){
            ex.printStackTrace();
        }
    }
}

class Consumer implements Runnable {
    ConcurrentLinkedQueue<String> queue;
    Consumer(ConcurrentLinkedQueue<String> queue){
        this.queue =queue;
    }
    
    public void run() {
        String str;
        System.out.println("Consumer started");
        for (int x=0; x<10; x++){
            while ((str = queue.poll()) != null) {
                System.out.println("Removed: " + str);
            }try {
                Thread.currentThread().sleep(1000);
            } catch (Exception ex){
                ex.printStackTrace();
            }
        }
    }
}        

Output:

Producer Started
Consumer started
Added: String1
Removed: String1
Added: String2
Removed: String2
Added: String3
Removed: String3
Added: String4
Removed: String4
Added: String5
Removed: String5
Added: String6
Removed: String6
Added: String7
Removed: String7
Added: String8
Removed: String8
Added: String9
Removed: String9        

20. Exchanger

Exchanger is a synchronization point at which treaads can pair and swap elements within pairs. Each thread presents some object on entry to the exchange method, matches with a partner thread and receives its partner's object on return. An exchanger may be viewed as a bidirectional form of a synchronous Queue.

for example:- 2 threads passing them an exchanger and a message. Each thread prints out its own message, then exchanges it with the otherthread, then prints out it's own new exchanged message.


21. Thread Factory

Thread Factory is an interface in the java.util.concurrent package that provides a method or creating new thread. It acts as a factory of threads, allowing developers to customize how threads are created, Such as setting thread names, priorities or daemon status.


22. Thread Local

The ThreadLocal class in Java enables you to create variables that can only be read and written by the same thread. Thus, even if two threads are executing the same code, and the code has a reference to a Thread Local variable, then the two threads can't see each other's ThreadLocal variables.

private ThreadLocal mythreadLocal = new ThreadLocal();

As you can see, you instantiate a new Thread Local Object. This only needs to be done once, and it doesn't matter which thread does that. All threads will see the same thread Local instance, but the values set on the ThreadLocal via its set() method will only be visible to the thread who set the value. Even if two different threads set different values on the same ThreadLocal object, they can't see each other's values.

Local Variables in the Thread:-

If you are working with the mutilthreaded programming, the volatile keyword will be more useful. When multiple threads using the same variable, each thread will have its own copy of the local cache for that variable. So, when it's updating the value, it is actually updated in the local cache not in hthe main variable memory. The other thread which is using the same variable doesn't know anything about the values changed by the other thread. To avoid this problem, if you declare a variable as volatile, then it will not be stored in the local cache. Whenever thread are updating the value, it is updated to the main memory. so, other thread can access the updated values.

The Java volatile keyword is used to mark a Java variable as "being stored in the main memory" . More precisely that menas, that every read of volatile variable will be reaad from the computer's main memory and not from the CPU cache, and that every write to a volatile variable will be written to main memory, and not just to the CPU Cache.


To view or add a comment, sign in

More articles by Tushar Kumar

Explore content categories