A Complete MultiThreading Tutorial In Java

Introduction

The process of executing multiple threads concurrently is called Multithreading in Java. This article will explain Multithreading and how Multithreading works in Java with code examples. In Java, threads use a shared memory location. 

What is Thread in Java?

A thread is a lightweight small process or the smallest unit of the processing, which can run concurrently with the other threads of the same process.

Threads are independent because they all have a separate path of execution due to which if an exception occurs in one thread, it doesn't affect the execution of other threads. All the threads of a process share a common memory area. The process of executing multiple threads is called Multithreading in Java.

Let's understand the figure, given below.

java-threads 

In the figure, a thread is executed inside the process, and the context-switching between the threads. Here the multiple processes in the OS and the single process can have multiple threads but only one thread executed at a time.

Multithreading is a procedure of executing multiple threads at the same time. Multiprocessing and Multithreading are used to achieve multitasking in Java. Saving memory and context-switching between the threads takes less time than the process. Multithreading is mainly used in games, animation, etc.

What are the Advantages of Multithreading in Java?

  • The threads are independent due to which we can perform multiple operations at the same time.
  • We can perform many operations simultaneously, so it saves time.
  • The Threads don't affect other threads if an exception occurs in a single thread.
  • Better use of CPU resources.
  • Decrease the cost of maintenance.
  • Better use of cache storage by utilization of resources.

What are the Disadvantages of Multithreading in Java?

  • Multiple threads can interfere with each other when sharing hardware resources such as caches or transaction look-aside buffers(TLBs). 
  • The execution time of a single thread can be degraded, even when only one thread is executing.
  • Complex debugging and testing process.
  • Unpredictable results.

Why do we use Multithreading in Java?

We use Multithreading in Java for achieving Multitasking. 

What is Multitasking in Java?

Multitasking is the process of executing multiple tasks at the same time. We use multitasking to utilize the CPU. 

Multitasking can be achieved in two ways. Which are

Process-based Multitasking  

Process-based multitasking means Multiprocessing, in this, every process has its own memory address and every process allocates a separate memory area. 

The process is heavy-weight. The cost of communication during the process is very high. Switching from one process to another process requires some time to save and load the registers, memory maps, updates, lists, etc.

Thread-based Multitasking

Thread-based multitasking means Multithreading. in which all the threads share the same address space.

The thread is lightweight. The cost of communication between the threads is very low. At least one process is required for each thread.

What is thread life-cycle in Java? 

A thread goes through many different stages in its life cycle. For example, a thread is born, the thread starts, the thread runs, and then the thread dies. The life cycle of the thread is controlled by JVM in Java. For a better understanding of the thread, we explain it in the five states.

java-thread-lifecycle

New

A new state is the first state of the thread. The thread is in the new state if we create an object of the Thread class but before the invocation of the start() method. 

Runnable 

After a new thread starts, the thread is in the second state after the invocation of the start() method, but the scheduler has not selected it to be in the running thread.

Running 

The third state is running. The thread is in a running state if the thread scheduler has selected it.

Non-Runnable (Blocked) 

The fourth state is non-runnable(blocked). This is the state when the thread is alive but is currently not eligible to run. 

Terminated 

The last state of a thread is terminated. A thread is in the terminated state when its run() method exits. 

How threads are created in Java? 

Threads are created in Java in two ways, first by extending the Thread class and second by implementing the Runnable interface by any Java class.

How many ways create a thread in Java?

There are two ways to create a thread in Java multithreading.

  1. By Extending the Thread class 
  2. By implementing the Runnable method 

What is the Thread class in Multithreading? 

Thread class provides many constructors and methods to create and perform many operations on a  thread. Thread class extends an Objects class and implements a Runnable interface in Java multithreading.

Constructors of Thread class

1. Thread()

Allocates a new Thread object. The complete program is listed below.

public class ThreadConstructor extends Thread {  
    public void run() {  
        System.out.println("This is example of Thread Constructor");  
    }  
  
    public static void main(String args[]) {  
        ThreadConstructor t = new ThreadConstructor();  
        t.start();  
    }  
} 

The output of the following code generates the following output.

thread-constructor

2. Thread(String name)

Allocates a new Thread object. The complete program is listed below.

public class ThreadConstructor1 extends Thread{  
    String tname;  
  
    ThreadConstructor1(String tname) {  
        this.tname = tname;  
    }  
  
    public void run() {  
        System.out.println(tname);  
    }  
  
    public static void main(String args[]) {  
        ThreadConstructor1 t = new ThreadConstructor1("First Thread");  
        t.start();  
    }  
} 

The output of the following code generates the following output.

thread-constructor1

3. Thread(Runnable r)

Allocates a new Thread object. The complete program is listed below.

public class ThreadConstructor2 implements Runnable {  
  
    public void run() {  
        System.out.println("This is the example of Thread(Runnable target) constructor");  
    }  
  
    public static void main(String args[]) {  
        ThreadConstructor2 s = new ThreadConstructor2();  
        Thread t = new Thread(s);  
        t.start();  
    }  
} 

The output of the following code generates the following output.

thread-constructor2

4. Thread(Runnable r, String name)

Allocates a new Thread object. The complete program is listed below. 

public class ThreadConstructor3 implements Runnable {  
  
    ThreadConstructor3() {  
    }  
  
    public void run() {  
        System.out.println("This is the example of Thread(Runnable target, String name) constructor");  
    }  
  
    public static void main(String args[]) {  
        ThreadConstructor3 s = new ThreadConstructor3();  
        Thread t = new Thread(s, "CsharpCorner");  
  
        t.start();  
    }  
} 

The output of the following code generates the following output.

thread-constructor3

Methods of the Thread class in Java

1. public void run()

This method is used to perform an action for a thread. The complete program is listed below.

public class ThreadRunMethod extends Thread {  
    public void run() {  
        System.out.println("This is the example of run() method");  
    }  
  
    public static void main(String args[]) {  
        ThreadRunMethod t = new ThreadRunMethod();  
        t.start();  
    }  
}

The output of the following code generates the following output.

thread-run-method

2. public void Thread.start()

This method is used to start the execution of the thread. JVM calls the run() method to start the thread. The complete program is listed below.

public class ThreadStartMethod extends Thread {  
  
    public void run() {  
        System.out.println("This is the example of start() method");  
    }  
  
    public static void main(String args[]) {  
        ThreadStartMethod s = new ThreadStartMethod();  
        s.start();  
    }  
}

The output of the following code generates the following output.

thread-start-method

3. public void Thread.sleep(long milliseconds)

The Thread class sleep() method is used to sleep a thread for a particular duration of time. If one thread sleeps for the specified time then the thread scheduler picks up other threads and so on. The complete program is listed below.

public class ThreadSleepMethod extends Thread {  
  
    public void run() {  
        for (int i = 0; i < 5; i++) {  
            try {  
                Thread.sleep(1000);  
            } catch (InterruptedException e) {  
                System.out.println(e);  
            }  
            System.out.println(i);  
        }  
    }  
  
    public static void main(String args[]) {  
        ThreadSleepMethod t1 = new ThreadSleepMethod();  
        ThreadSleepMethod t2 = new ThreadSleepMethod();  
        t1.start();  
        t2.start();  
    }  
} 

The output of the following code generates the following output.

thread-sleep-method1

4. public void Thread.join()

The join method is used to wait for a thread to die. The complete program is listed below.

public class ThreadJoinMethod1 extends Thread {  
  
    public void run() {  
        for (int i = 0; i <= 3; i++) {  
            try {  
                Thread.sleep(1000);  
            } catch (Exception e) {  
                System.out.println(e);  
            }  
            System.out.println(i);  
        }  
    }  
  
    public static void main(String args[]) {  
        ThreadJoinMethod1 t1 = new ThreadJoinMethod1();  
        ThreadJoinMethod1 t2 = new ThreadJoinMethod1();  
        ThreadJoinMethod1 t3 = new ThreadJoinMethod1();  
        t1.start();  
        try {  
            t1.join();  
        } catch (Exception e) {  
            System.out.println(e);  
        }  
  
        t2.start();  
        t3.start();  
  
        System.out.println("This is Thread join() method example");  
    }  
} 

The output of the following code is listed below.

thread-join-method

5. public void Thread.join(long milliseconds)

This method is used to wait for a thread to die for a specified millisecond. The complete program is listed below.

public class ThreadJoinMethod2 extends Thread {  
  
    public void run() {  
        for (int i = 0; i <= 3; i++) {  
            try {  
                Thread.sleep(700);  
            } catch (Exception e) {  
                System.out.println(e);  
            }  
            System.out.println(i);  
        }  
    }  
  
    public static void main(String args[]) {  
        ThreadJoinMethod2 t1 = new ThreadJoinMethod2();  
        ThreadJoinMethod2 t2 = new ThreadJoinMethod2();  
        ThreadJoinMethod2 t3 = new ThreadJoinMethod2();  
        t1.start();  
        try {  
            t1.join(1000);  
        } catch (Exception e) {  
            System.out.println(e);  
        }  
        t2.start();  
        t3.start();  
        System.out.println("This is Thread join() method example");  
  
    }  
} 

The output of the following code generated the following output.

thread-join-method2

6. public void Thread.currentThread()

The thread class static method currentThread() returns the reference of the Thread object that calls this method. The complete program is listed below.

public class ThreadCurrentMethod extends Thread {    
    
        @Override    
        public void run() {    
            Thread t = Thread.currentThread();    
            String threadName = t.getName();    
            System.out.println("Inside run() method:  " + threadName);    
        }    
    
        public static void main(String[] args) {    
            ThreadCurrentMethod ct = new ThreadCurrentMethod ();    
            ct.start();    
    
            Thread t = Thread.currentThread();    
            String threadName = t.getName();    
            System.out.println("Inside  main() method:  " + threadName);    
        }    
    }   

The output of the following code generates the following output.

thread-current-method.

7. public String Thread.getName()

This method is used to return the name of the thread. The complete program is listed below.

public class ThreadGetnameMethod extends Thread {  
  
    public void run() {  
        System.out.println("threads running fast...");  
    }  
  
    public static void main(String args[]) {  
        ThreadGetnameMethod t1 = new ThreadGetnameMethod();  
  
        System.out.println("Name of thread 1:" + t1.getName());  
        t1.start();  
  
    }  
}

The output of the following code generates the following output.

thread-getname-method

8. public String Thread.setName(String name)

This method is used to change the name of the thread. The complete program is listed below.

public class ThreadSetnameMethod extends Thread {  
  
    public void run() {  
        System.out.println("threads running fast...");  
    }  
  
    public static void main(String args[]) {  
        ThreadSetnameMethod t1 = new ThreadSetnameMethod();  
        System.out.println("Name of thread 1: " + t1.getName());  
        t1.start();  
  
        t1.setName("Strong Thread");  
        System.out.println("After changing name of thread 1:" + t1.getName());  
    }  
} 

The output of the code generates the following output.

thread-setname-method

9. public int Thread.currentThread().getPriority()

This method is used to return the priority of the thread. The complete program is listed below.

public class ThreadGetPriorityMethod extends Thread {  
  
    public void run() {  
  
        System.out.println("Current thread priority is:" + Thread.currentThread().getPriority());  
    }  
  
    public static void main(String args[]) {  
        ThreadGetPriorityMethod g = new ThreadGetPriorityMethod();  
        g.start();  
    }  
} 

The output of the following code generates the following output.

thread-getpriority-method

10. public void Thread.currentThread().setPriority(int priority)

This method is used to change the priority of the thread. The complete program is listed below.

public class ThreadSetPriorityMethiod extends Thread {  
  
    public void run() {  
        System.out.println("Current thread priority is:" + Thread.currentThread().getPriority());  
    }  
  
    public static void main(String args[]) {  
        ThreadSetPriorityMethiod s = new ThreadSetPriorityMethiod();  
        s.setPriority(8);  
        s.start();  
    }  
} 

The output of the following code generates the following output.

thread-setPriority-method

What is the Runnable interface in Java? 

The runnable interface must be implemented by any class, whose instances are intended to be executed by a thread. The Runnable interface has only one run() method.

public void run()

This method is used to perform an action for a thread. The complete program is listed below.

public class RunnableRunMethod implements Runnable {  
  
    @Override  
    public void run() {  
        System.out.println("This is the example of Runnable interface run() method");  
    }  
  
    public static void main(String[] args) {  
        RunnableRunMethod m = new RunnableRunMethod();  
        Thread t = new Thread(m);  
        t.start();  
    }  
}   

The output of the following code generates the following output.

runnable-run-method 

How to start a thread in Java?

start() method is used to start a newly created thread. It performs may tasks, Which are-

  • A new thread start.
  • The thread shifts from the new state to the runnable state.
  • If the thread gets a chance to execute, its target run() method will run. 

The complete program is listed below. 

public class ThreadExample extends Thread {  
  
    public void run() {  
        System.out.println("This is example of how to start a Thread in Java.");  
    }  
  
    public static void main(String args[]) {  
        ThreadExample t = new ThreadExample();  
        t.start();  
    }  
} 

The output of the following code generates the following output.

thread-example 

Can We start a thread twice in Java?

No, we cannot start a thread twice, after starting a thread, it can never be started again. If we do then all IllegalThreadStateException is thrown. In this case, the thread will run once but next time, it will throw an exception. The complete program is listed below.

public class StartThreadTwice extends Thread {  
  
    public void run() {  
        System.out.println("we can not start a thread twice in a program");  
    }  
  
    public static void main(String args[]) {  
        ThreadExample t1 = new ThreadExample();  
        t1.start();  
        t1.start();  // Exception  
    }  
}  

The output of the following code generates the following output.

start-thread-twice

What is the Thread scheduler in Java? 

The thread scheduler is part of JVM, which decides which thread should run. There is no assurance that which runnable thread will be chosen to run by the scheduler. Any thread in the runnable state can be chosen by the scheduler to be the one and only running thread and if a thread is not in a runnable state, then it cannot be chosen to be the currently running thread. Some methods can influence the scheduler to some extent and we can't control the behavior of the thread scheduler. Only one thread at a time can run in a single process in Java.

The thread scheduler generally uses preemptive scheduling or time-slicing scheduling to schedule the threads in Java. 

Difference between Preemptive Scheduling and Time-slicing scheduling

Preemptive Scheduling Time-slicing Scheduling
Under preemptive scheduling, the highest priority tasks perform until it enters the waiting or dead state or a higher priority task comes into existence. Under the time-slicing scheduling, a task performs for a predefined slice of time and then reenters the pool of ready tasks. The thread scheduler then decides which task should execute next, based on the priority and other factors.

A priority of Thread in Java 

Thread priorities are represented in the number 1 to 10. The thread scheduler schedules the threads, according to their priority, which is referred to as preemptive scheduling. There is no guarantee because it fully depends on the JVM specification that which scheduling, it chooses.

Each thread has a priority. 

Three constants are defined in Thread class,  

  1. public static int MIN_PRIORITY
  2. public static int NORM_PRIORITY
  3. public static int MAX_PRIORITY

By default thread priority is NORM_PRIORITY(5)

The value of MIN_PRORITY is 0 and MAX_PRIORITY is 10. 

The complete program is given below.

public class ThreadPriority extends Thread {  
    public void run() {  
        System.out.println("Current thread name is:" + Thread.currentThread().getName());  
        System.out.println("Current thread priority is:" + Thread.currentThread().getPriority());  
    }  
  
    public static void main(String args[]) {  
        ThreadPriority p1 = new ThreadPriority();  
        p1.setPriority(Thread.MIN_PRIORITY);  
        p1.start();  
    }  
} 

The output of the following code generates the following output.

thread-priority

What is Thread Pool in Java?

Thread Pool is a group of worker threads, which are waiting for the job and can be reused many times.

A group of fixed-size threads is created in a thread pool. A thread from the thread pool is selected and assigned a job by the Service provider. After completion of his job, the thread is contained in the thread pool again.

What are the usages of the thread pool in Java?

The thread pool is mainly used in JSP and Servlets in Java, in which the web or Servlets container creates a thread pool to process the request.

It saves time and better performance because there is no need to create a new thread. The complete program is listed below.

Step 1. First, we create a ThreadPoolExample Java class and implement the Runnable interface. 

class ThreadPoolExample implements Runnable {  
    private String thread;  
  
    public ThreadPoolExample(String t) {  
        this.thread = t;  
    }  
  
    public void run() {  
        System.out.println(Thread.currentThread().getName() + " Start thread = " + thread);  
        processmessage();//sleeps the thread for 2 seconds  
        System.out.println(Thread.currentThread().getName() + " End ");//prints thread name  
    }  
  
    private void processmessage() {  
        try {  
            Thread.sleep(1500);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
    }  
}  

Step 2. Second, we create the ThreadPoolTest Java class for testing the working of the threadpool.

import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
  
public class ThreadPoolTest {  
    public static void main(String[] args) {  
        ExecutorService e = Executors.newFixedThreadPool(3);//creating a pool of 3 threads  
        for (int i = 0; i <= 6; i++) {  
            Runnable t = new ThreadPoolExample("" + i);  
            e.execute(t);  
        }  
        e.shutdown();  
        while (!e.isTerminated()) {  
        }  
        System.out.println("All threads are finish");  
    }  
}  

The output of the code generates the following output.

threadpool 

What is the ThreadGroup class in Java?

A group of multiple threads in a single object is called a ThreadGroup class in Java. With the help of ThreadGroup class, we can suspend, resume or interrupt a group of threads with a single method call. 

ThreadGroup is implemented by Java.lang.ThreadGroup class. The complete program is listed below.

public class ThreadGroupExample implements Runnable {  
    public void run() {  
        System.out.println(Thread.currentThread().getName());  
    }  
  
    public static void main(String[] args) {  
        ThreadGroupExample obj = new ThreadGroupExample();  
        ThreadGroup tg = new ThreadGroup("Main ThreadGroup");  
        Thread t1 = new Thread(tg, obj, "thread1");  
        t1.start();  
        Thread t2 = new Thread(tg, obj, "thread2");  
        t2.start();  
        Thread t3 = new Thread(tg, obj, "thread3");  
        t3.start();  
        System.out.println("Name of ThreadGroup : " + tg.getName());  
        tg.list();  
    }  
}  

The output of the following code generates the following output.

threadgroup 

Note

In this example, we create three threads that belong to one group. Here, tg is the ThreadGroup name, The ThreadGroupExample class implements the Runnable interface, and thread1, thread2, and thread3 are the threads name. 

What is Daemon Thread in Java? 

Daemon thread is a service provider, which provides services to the user thread. It is a low-priority thread, which runs in the background to perform the tasks. Its life depends on the mercy of the user threads, i.e When all the threads die, JVM automatically terminates this thread. 

There are many Java daemon threads running automatically such as garbage collection(gc) finalizer etc.

Some important points for Daemon Thread,

  • It provides the services to the user thread for the background supporting tasks. It has no role in life than to serve the user thread. 
  • Its life fully depends on user threads.
  • It is a low-priority thread. 

Why JVM terminates the Daemon thread if there is no user thread? 

The main purpose of the daemon thread is to provide the services to the user threads for the background supporting tasks due to which if there is no user thread, JVM terminates the Daemon thread.

The complete program of a daemon thread is listed below. 

public class DaemonThreadExample extends Thread {  
        public void run() {  
            if (Thread.currentThread().isDaemon()) {//checking for a daemon thread  
                System.out.println("I am Daemon thread");  
            } else {  
                System.out.println("I am User thread");  
            }  
        }  
  
        public static void main(String[] args) {  
            DaemonThreadExample t1 = new DaemonThreadExample();  
            DaemonThreadExample t2 = new DaemonThreadExample();  
            t1.setDaemon(true);//it's a daemon thread  
            t1.start();  
            t2.start();  
        }  
    }  

The output of the code generates the following output.

Daemon-thread-example

If we want to create a User-defined Daemon thread, it should not be started, else it will throw an exception IllegalThreadStateException. The complete program of the User-defined Daemon thread is listed below. 

public class UserDefinedDaemon extends Thread {  
    public void run() {  
        System.out.println("Name: " + Thread.currentThread().getName());  
        System.out.println("Daemon: " + Thread.currentThread().isDaemon());  
    }  
  
    public static void main(String[] args) {  
        UserDefinedDaemon t1 = new UserDefinedDaemon();  
        t1.start();  
        t1.setDaemon(true);//throw exception  
    }  
}  

The output of the following code generates the following output.

user-defined-daemon 

How to perform a single task with multiple threads?

If we want to perform a single task by many threads, we need to use only one run() method. The complete program is listed below. 

public class MultipleThreadsingleTask extends Thread{  
    public void run() {  
        System.out.println("Task one is start now..");  
    }  
  
    public static void main(String args[]) {  
        MultipleThreadsingleTask mt1 = new MultipleThreadsingleTask();  
        MultipleThreadsingleTask mt2 = new MultipleThreadsingleTask();  
        MultipleThreadsingleTask mt3 = new MultipleThreadsingleTask();  
        mt1.start();  
        mt2.start();  
        mt3.start();  
    }  
}       

The output of the following code generates the following output.

multiple-thread-singletask 

Note

In the example, we created MultipleThreads class extends the Thread class, and performs a single task through the multiple threads.

How to perform multiple tasks by multiple threads in Java?

If we want to perform multiple tasks by multiple threads, we need to use the multiple run() method. The complete program is listed below. 

class Multi1 extends Thread {  
  
    public void run() {  
        System.out.println("Task one is start now...");  
    }  
}  
  
class Multi2 extends Thread {  
    public void run() {  
        System.out.println("Task two is start now...");  
    }  
}  
  
public class MultipleThreadsMultipleTask {  
    public static void main(String args[]) {  
        Multi1 t1 = new Multi1();  
        Multi2 t2 = new Multi2();  
  
        t1.start();  
        t2.start();  
    }  
}       

The output of the following code generates the following output.

multiple-thread-multiple-task 

How to use the Anonymous class with Runnable interface?

The classes which have no name are called Anonymous classes in Java. We can declare and initiate them at the same time. The complete program is listed below. 

public class AnonymousClassExample {  
  
    public static void main(String args[]) {  
          
        Runnable r1 = new Runnable() {  
  
            public void run() {  
                System.out.println("Task one is start now...");  
            }  
        };  
        Runnable r2 = new Runnable() {  
              
            public void run() {  
                System.out.println("Task two is start now...");  
            }  
        };  
          
        Thread t1 = new Thread(r1);  
        Thread t2 = new Thread(r2);  
        t1.start();  
        t2.start();  
    }  
}  

The output of the following code generates the following output.

anonymous_class_example 

How to use the Anonymous class that extends the Thread class in Java?

The complete program of the anonymous class that extends the Thread class in Java. 

public class AnonymousClassExample {  
  
    public static void main(String args[]) {  
  
        Thread t1 = new Thread() {  
              
            public void run() {  
                System.out.println("Task one is start now...");  
            }  
        };  
        Thread t2 = new Thread() {  
  
            public void run() {  
                System.out.println("Task two is start now...");  
            }  
        };  
  
        t1.start();  
        t2.start();  
    }  
}  

The output of the following code generates the following output.

anonymous-class-thread

What happens when we call the run() method instead of the start() method in Multithreading? 

Every thread starts in a separate call stack. Calling the run() method from the main thread, followed by the run() method goes on the present call stack rather than at the beginning of the new call stack.

There will be no context-switching between the threads. The complete program is listed below. 

public class RunInsteadOfStart extends Thread {  
    public void run() {  
  
        for (int i = 0; i < 3; i++) {  
            try {  
                Thread.sleep(500);  
            } catch (InterruptedException e) {  
                System.out.println(e);  
            }  
            System.out.println(i);  
        }  
    }  
  
    public static void main(String args[]) {  
  
        RunInsteadOfStart t1 = new RunInsteadOfStart();  
        RunInsteadOfStart t2 = new RunInsteadOfStart();  
        t1.run();  
        t2.run();  
    }  
}

The output of the following code generates the following output.

run-instead-start

Note

In this example, we can see that there is no context-switching because here t1 and t2 will treat as normal objects and not thread objects.

What is Thread Synchronization?

In Java, synchronization is the capacity to control the access of multiple threads to any shared resources. It is a good option when we want to allow one thread to access the shared resources. 

There are 2 types of Thread synchronization.

1) Mutual exclusive

Mutual exclusive helps to keep the threads from interfering with one another as a sharing data. This can be done in three ways.

a. By synchronized method

When we create any method as synchronized, it is called a synchronized method. The synchronized method locks an object for any shared resources. When a thread calls a synchronized method, it automatically gets the lock for that object and frees it when the thread completes its task. The complete program is listed below. 

class Customer {  
    int amount = 10000;  
  
    synchronized void withdraw(int amount) {  
        System.out.println("going to withdraw...");  //Synchronized Method
  
        if (this.amount < amount) {  
            System.out.println("Less balance; waiting for deposit...");  
            try {  
                wait();  
            } catch (Exception e) {  
  
            }  
        }  
        this.amount -= amount;  
        System.out.println("withdraw completed...");  
    }  
  
    synchronized void deposit(int amount) {  
        System.out.println("going to deposit...");  
        this.amount += amount;  
        System.out.println("deposit completed... ");  
        notify();  
    }  
}  
  
public class SynchronizedMethodExample {  
    public static void main(String args[]) {  
        final Customer c = new Customer();  
  
        new Thread() {  
            public void run() {  
                c.withdraw(15000);  
            }  
        }.start();  
  
        new Thread() {  
            public void run() {  
                c.deposit(10000);  
            }  
        }.start();  
  
    }  
} 

The output of the following code generates the following output.

synchronized-method

b. By synchronized block

A block in Java is a group of one or more statements enclosed in braces. when we used a synchronized keyword with a block, it is called a synchronized block. The complete program is listed below.

class Table {  
  
    void printTable(int n) {  
        synchronized (this) {//synchronized block  
            for (int i = 1; i <= 5; i++) {  
                System.out.println(n * i);  
                try {  
                    Thread.sleep(400);  
                } catch (Exception e) {  
                    System.out.println(e);  
                }  
            }  
        }  
    }//end of the method  
}  
  
class MyThreadClass1 extends Thread {  
    Table t;  
  
    MyThreadClass1(Table t) {  
        this.t = t;  
    }  
  
    public void run() {  
        t.printTable(5);  
    }  
  
}  
  
class MyThreadClass2 extends Thread {  
    Table t;  
  
    MyThreadClass2(Table t) {  
        this.t = t;  
    }  
  
    public void run() {  
        t.printTable(100);  
    }  
}  
  
public class SynchronizedBlockExample {  
  
    public static void main(String args[]) {  
        Table obj = new Table();//only one object  
        MyThreadClass1 t1 = new MyThreadClass1(obj);  
        MyThreadClass2 t2 = new MyThreadClass2(obj);  
        t1.start();  
        t2.start();  
    }  
} 

The output of the following code generates the following output.

synchronized-block-example

c. By static synchronized

If we make any static method synchronized, the lock will be on the class not on the object. The complete program is listed below.

class PrintTable {  
    public synchronized static void printTable(int n) {  
        System.out.println("Table of " + n);       // static Synchroized


        for (int i = 1; i <= 10; i++) {  
            System.out.println(n * i);  
            try {  
                Thread.sleep(500);  
            } catch (Exception e) {  
                System.out.println(e);  
            }  
        }  
    }  
}  
  
class MyThread1 extends Thread {  
    public void run() {  
        PrintTable.printTable(2);  
    }  
}  
  
public class StaticSynchronizedExample {  
    public static void main(String args[]) {  
  
        //creating threads.  
        MyThread1 t1 = new MyThread1();  
          
        //start threads.  
        t1.start();  
    }  
} 

The output of the following code generates the following output.

static-synchronized

Cooperation (interthread communication) in Java

Cooperation (interthread communication) is all about allowing synchronized threads to communicate with each other. It is a mechanism in which a thread is paused running in its critical section and another thread is allowed to enter the same critical section to be executed. 

Java provides the benefit of avoiding thread pooling using interthread communication. The complete program of interthread communication is listed below.

class Messages {  
    boolean flag = false;  
  
    public synchronized void Question(String msg) {  
        if (flag) {  
            try {  
                wait();  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
        System.out.println(msg);  
        flag = true;  
        notify();  
    }  
  
    public synchronized void Answer(String msg) {  
        if (!flag) {  
            try {  
                wait();  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
  
        System.out.println(msg);  
        flag = false;  
        notify();  
    }  
}  
  
class T1 implements Runnable {  
    Messages m;  
    String[] s1 = {"Hi", "How are you ?", "I am also doing fine!"};  
  
    public T1(Messages m1) {  
        this.m = m1;  
        new Thread(this, "Question").start();  
    }  
  
    public void run() {  
        for (int i = 0; i < s1.length; i++) {  
            m.Question(s1[i]);  
        }  
    }  
}  
  
class T2 implements Runnable {  
    Messages m;  
    String[] s2 = {"Hi", "I am good, what about you?", "Great!"};  
  
    public T2(Messages m2) {  
        this.m = m2;  
        new Thread(this, "Answer").start();  
    }  
  
    public void run() {  
        for (int i = 0; i < s2.length; i++) {  
            m.Answer(s2[i]);  
        }  
    }  
}  
  
public class InterThreadCommunicationExample {  
    public static void main(String[] args) {  
        Messages m = new Messages();  
        new T1(m);  
        new T2(m);  
    }  
} 

The output of the following code generates the following output.

inter-thread-communication 

What is Lock Concept in Multithreading?

Synchronization is built around an internal entity called the lock or monitor. All objects have a lock associated with it. By rule, a thread that needs consistent access to an object's fields to get the object's lock before accessing them and releasing the lock, when it's done with them. Since Java 5 the package java.util.concurrent.locks contain many lock implementations.

The complete program is listed below.

class A {  
    void print(int n) {  
        for (int i = 1; i <= 5; i++) {  
            System.out.println(n * i);  
            try {  
                Thread.sleep(1000);  
            } catch (Exception e) {  
                System.out.println(e);  
            }  
        }  
    }  
}  
class MyThreadLock1 extends Thread {  
    A a;  
    MyThreadLock1(A a) {  
        this.a = a;  
    }  
    public void run() {  
        a.print(1);  
    }  
}  
class MyThreadLock2 extends Thread {  
    A a;  
  
    MyThreadLock2(A a) {  
        this.a = a;  
    }  
    public void run() {  
        a.print(100);  
    }  
}  
public class LockConceptExample {  
    public static void main(String args[]) {  
        A obj = new A();  
        MyThreadLock1 t1 = new MyThreadLock1(obj);  
        MyThreadLock2 t2 = new MyThreadLock2(obj);  
        t1.start();  
        t2.start();  
    }  
} 

The output of the following code generates the following output.

lock-concept

What is Thread Deadlock in Java?

Deadlock is a situation of complete Lock when no thread can complete its execution because of a lack of resources. Deadlock occurs when multiple threads need the same locks but obtain them in a different order. Since both threads are waiting for each other to release the lock, this condition is called deadlock.

java-thread-deadlock

The complete program is listed below.

class Pen{}  
class Paper{}  
  
public class DeadLockExample {  
  
    public static void main(String[] args)  
    {  
        final Pen pn =new Pen();  
        final Paper pr =new Paper();  
  
        Thread t1 = new Thread() {  
            public void run()  
            {  
                synchronized(pn)  
                {  
                    System.out.println("Thread1 is holding Pen");  
                    try{  
                        Thread.sleep(1000);  
                    }  
                    catch(InterruptedException e){  
                        // do something  
                    }  
                    synchronized(pr)  
                    {  
                        System.out.println("Requesting for Paper");  
                    }  
                }  
            }  
        };  
        Thread t2 = new Thread() {  
            public void run()  
            {  
                synchronized(pr)  
                {  
                    System.out.println("Thread2 is holding Paper");  
                    try {  
                        Thread.sleep(1000);  
                    }  
                    catch(InterruptedException e){  
                        // do something  
                    }  
                    synchronized(pn)  
                    {  
                        System.out.println("requesting for Pen");  
                    }  
                }  
            }  
        };  
  
        t1.start();  
        t2.start();  
    }  
}  

The output of the following code generates the following output.

deadlockpro

Summary 

In this tutorial, we learned about Multithreading in Java and how to achieve Multithreading in our Java programs.   

If you require any clarification/suggestions on the article, please leave your questions and thoughts in the comment section below. Follow C# Corner to learn more new and amazing things about Java Programming or to explore more technologies.

Thanks for reading, and I hope you like it.


Similar Articles