Over

120,000

Worldwide

Saturday - Sunday CLOSED

Mon - Fri 8.00 - 18.00

Call us

 

Java Multithreading

.

Contents

Java Multithreading  1

1.  Introduction  2

2.  Advantages  2

3.  Thread Lifecycle  2

4.  Thread Priority  3

5.  Implementation of Multithreading  3

5.1  Implementation using Thread Class  3

5.2  Implementation using Runnable Interface  5

5.3  Thread Class vs Runnable Interface  6

6.  Thread Group  6

7.  Synchronization  7

7.1  Synchronized Method  8

7.2  Synchonized Block  8

7.3  Static Synchronization  9

8.  Inter-Thread Communication  11

9.  ExecutorService  12

.

.

.

.

.

.

.

.

.

.

.

.

.

1.Introduction

.

 A thread is the smallest and lightweight process in an application.
 Multithreading is the process of executing multiple threads concurrently in an application.
 In simple language, multithreading helps to run applications faster by executing multiple processes at the same time.

.

2.Advantages

.

 Multiple threads use shared memory, which saves space and allows faster switching between threads
 Better utilization of CPU
 Threads are independent
 Improved user experience

.

3.Thread Lifecycle

.

Following are the stages of life cycle of a thread:

.

C:\Users\ansingha.AD-ONE\Downloads\aa.png

.

 NEW: The thread is created (implemented) but not yet started by the program.
 RUNNABLE: The instance of the thread is invoked using the start method and given to scheduler for execution.
 RUNNING: The thread is executing and performing its task.
 BLOCKED: The thread is waiting or blocked while another thread completes its execution.
 TERMINATED: The thread has completed its execution.
4.Thread Priority

.

 Each thread has a priority that helps the scheduler in ordering of the execution of threads.
 Priority is represented by a number from 1 to 10; where 1 is the MIN_PRIORITY, 5 the NORM_PRIORITY (also default) and 10 the MAX_PRIORITY
 Thread priority does not guarantee the order of execution of threads as it is dependent on the scheduling method chosen by the JVM.

.

5.Implementation of Multithreading
5.1Implementation using Thread Class

.

1.Create a class that is to executed as a thread and extend the java.lang.Thread class
2.Override and provide functionality implementation inside the run() method
3.Call start() method on the object of the class to submit the thread for execution.

.

Some important methods in the Thread class:

 start(): start a new thread; it internally invokes the run() method.
 run(): execution of the thread starts from this method. If run() method is called directly, a new thread will not be created rather the call goes to the same stack.
 currentThread(): returns the reference to current thread.
 getName(): returns the name of the current thread
 sleep(long time): suspends the thread for given time duration (in milliseconds)
 yield(): pause the current thread and allow other threads to execute
 join(): current thread invokes this method on second thread, and is blocked until the second thread completes execution.
 isAlive(): checks if the current thread is alive or terminated.

.

.

Example:

.

In this example, we create two threads and each thread executes a loop containing a print statement five times:

.

.

public class ThreadExample extends Thread {
@Override
public void run() {

try {
//get the ID of current thread
long currentThreadId = Thread.currentThread().getId();

for(int i = 0 ; i<5 ; i++)
{

System.out.println(LocalDateTime.now() +

                                        “Running Thread ” + currentThreadId);
//suspend the current thread for two seconds
Thread.sleep(2000);
}

System.out.println(“Exiting Thread ” + currentThreadId);
}
catch (InterruptedException e) {
e.printStackTrace();

}

}

}

.

.

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

    try {

       //create two threads
       ThreadExample thread1 = new ThreadExample();
      thread1.start();

       ThreadExample thread2 = new ThreadExample();
       thread2.start();

      thread1.join();
     thread2.join();

      } catch (InterruptedException e) {
            e.printStackTrace();
      }

}
}

Output:

.

2021-05-25T10:22:22.491313700Running Thread 14

2021-05-25T10:22:22.491313700Running Thread 13

2021-05-25T10:22:24.590311600Running Thread 13

2021-05-25T10:22:24.590311600Running Thread 14

2021-05-25T10:22:26.592790300Running Thread 14

2021-05-25T10:22:26.592790300Running Thread 13

2021-05-25T10:22:28.598846700Running Thread 14

2021-05-25T10:22:28.598846700Running Thread 13

2021-05-25T10:22:30.606945900Running Thread 13

2021-05-25T10:22:30.606945900Running Thread 14

Exiting Thread 14

Exiting Thread 13

.

.

.

5.2Implementation using Runnable Interface

.

1.The class that is to executed as a thread implements the java.lang.Runnable interface and overrides run() method provided by the interface.
2.Instantiate an object of Thread class, where the constructor parameter is the object of class created in previous step
3.Call start() method on the object created in previous step

.

Example:

.

In this example, we create two threads and each thread executes a loop containing a print statement five times:

.

.

public class RunnableExample implements Runnable {
@Override
public void run() {

try {
//get the ID of current thread
long currentThreadId = Thread.currentThread().getId();

for(int i = 0 ; i<5 ; i++)
{

System.out.println(LocalDateTime.now() + ” Running Thread ”

+ currentThreadId);
//suspend the thread for two seconds
Thread.sleep(2000);
}

System.out.println(“Exiting Thread ” + currentThreadId);
}
catch (InterruptedException e) {
e.printStackTrace();

}

}

}

.

.

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

//create two threads
Thread thread1 = new Thread(new RunnableExample());
Thread thread2 = new Thread(new RunnableExample());
thread1.start();
thread2.start();
}

}

.

Output:

.

2021-05-24T11:38:29.830698800 Running Thread 14

2021-05-24T11:38:29.830698800 Running Thread 13

2021-05-24T11:38:31.904702900 Running Thread 14

2021-05-24T11:38:31.904702900 Running Thread 13

2021-05-24T11:38:33.912460600 Running Thread 14

2021-05-24T11:38:33.912460600 Running Thread 13

2021-05-24T11:38:35.925101100 Running Thread 14

2021-05-24T11:38:35.927102700 Running Thread 13

2021-05-24T11:38:37.940861700 Running Thread 13

2021-05-24T11:38:37.940861700 Running Thread 14

Exiting Thread 14

Exiting Thread 13

.

.

1.3Thread Class vs Runnable Interface

.

 If we extend the Thread class, extending any other class is not possible because Java doesn’t support multiple inheritance.
 Thread class provides some utility methods that could be helpful in implementation.

.

6.Thread Group

.

 It is possible to group multiple threads in a single object using java.lang.ThreadGroup class.
 In this way, multiple threads can be managed (e.g. suspend, resume) by a single method call.
 Thread group is maintained as a tree in which every thread group except the topmost one has a parent.

.

Creating a Thread Group:

public ThreadGroup(String name)

public ThreadGroup(ThreadGroup parent, String name)

.

Some of the important methods:

 String getName(): returns the name of this thread group.
 ThreadGroup getParent(): returns the parent of this thread group.
 void checkAccess(): determines if the currently running thread has the permission to modify this thread group.
 int activeCount(): returns the number of active threads in this thread group and its subgroups.
 int enumerate(Thread[] list): copies into the given array all active threads in this thread group and its subgroups.
 int activeGroupCount(): returns the number of active groups in this thread group and its subgroups.
 void interrupt(): interrupts all the threads in this thread group.
 void destroy(): destroys this thread group and its subgroups.
7.Synchronization

.

 In multithreaded environment, multiple threads could try to access shared resources at the same time and it could produce undesirable results.
 Synchronization helps to ensure that only one thread accesses a shared resource at a given time.
 The keyword ‘synchronized’ is used to achieve synchronization at the block or method level.
 With synchronization, one thread acquires a lock on the object for executing particular code and then releases the lock for other threads to access it.

Let’s consider the following example to understand the need of synchronization.

The class ‘HelloExample’ contains a method to print the given name in particular format and multiple thread objects of class ‘ThreadExample’ access this method. It produces inconsistent output, so synchronization is required for the threads to print each name in expected format one at a time.

.

public class HelloExample {

public void print(String name) {
try {
System.out.println(“Hello..”);
Thread.sleep(2000);
System.out.println(name);
}
catch (InterruptedException e) {
e.printStackTrace();

}

}

}

.

public class ThreadExample extends Thread {

HelloExample helloExample;
String name;

ThreadExample(HelloExample helloExample, String name)
{

this.helloExample = helloExample;
this.name = name;
}


@Override
public void run() {
helloExample.print(name);
}

}

.

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

HelloExample helloExample = new HelloExample();
//create three threads
ThreadExample thread1 = new ThreadExample(helloExample, “Nilton”);
thread1.start();
        ThreadExample thread2 = new ThreadExample(helloExample, “Ankit”);
thread2.start();
ThreadExample thread3 = new ThreadExample(helloExample, “Amit”);
thread3.start();
}

}

.

Expected Output:

Hello..

Nilton

Hello..

Amit

Hello..

Ankit

.

Actual Output:

Hello..

Hello..

Hello..

Nilton

Ankit

Amit

.

7.1Synchronized Method

.

 The thread acquires lock on the whole method i.e. no other thread can access the method until the current thread has finished its execution.

.

public class HelloExample {

public synchronized void print(String name) {
try {
System.out.println(“Hello..”);
Thread.sleep(2000);
System.out.println(name);
}
catch (InterruptedException e) {
e.printStackTrace();

}

}

}

Output:

Hello..

Nilton

Hello..

Amit

Hello..

Ankit

.

7.2Synchonized Block

.

 The thread acquires lock on a part of the method i.e. specific lines of code inside the block ; so other threads can still access the method and execute the rest of the code.

public class ThreadExample extends Thread {

HelloExample helloExample;
String name;

ThreadExample(HelloExample helloExample, String name)
{

this.helloExample = helloExample;
this.name = name;
}


@Override
public void run() {
synchronized (helloExample) {
helloExample.print(name);
}

}

}

Output:

Hello..

Nilton

Hello..

Amit

Hello..

Ankit

.

.

7.3Static Synchronization

.

 If we use synchronized on a static method, then the lock is acquired on the class and not on the object. This means when the shared resource belongs to all instances of a class i.e. it is static, then we use static synchronization.

Example:

Now we modify the previous example and create two objects of ‘HelloExample’ to be shared by four threads. Since the lock is on the objects ‘helloExample1’ and ‘helloExample2’, threads ‘thread0’ and ‘thread1’ will not interfere, and threads ‘thread2’ and ‘thread3’ will not interfere.

public class HelloExample {

public synchronized void print(String name) {
try {
System.out.println(Thread.currentThread().getName() + ” Hello..”);
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + ” ” + name);
}
catch (InterruptedException e) {
e.printStackTrace();

}

}

}

.

public class ThreadExample extends Thread {

HelloExample helloExample;
    String name;

ThreadExample(HelloExample helloExample, String name)
{

this.helloExample = helloExample;
this.name = name;
}


@Override
public void run() {
helloExample.print(name);
}

}

.

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

HelloExample helloExample1 = new HelloExample();
HelloExample helloExample2 = new HelloExample();

ThreadExample thread0 = new ThreadExample(helloExample1, “Nilton”);
thread0.start();
ThreadExample thread1 = new ThreadExample(helloExample1, “Ankit”);
thread1.start();
ThreadExample thread2 = new ThreadExample(helloExample2, “Amit”);
thread2.start();
ThreadExample thread3 = new ThreadExample(helloExample2, “Jack”);
thread3.start();

}
}

.

Output:

Thread-3 Hello..

Thread-0 Hello..

Thread-0 Nilton

Thread-1 Hello..

Thread-3 Jack

Thread-2 Hello..

Thread-2 Amit

Thread-1 Ankit

.

We get inconsistent output, because ‘thread0’ or ‘thread1’ can still interfere with ‘thread2’ or ‘thread3’ because they acquire lock on different objects.

To fix this, we use static synchronization:

public class HelloExample {

public static synchronized void print(String name) {
try {
System.out.println(Thread.currentThread().getName() + ” Hello..”);
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + ” ” + name);
}
catch (InterruptedException e) {
e.printStackTrace();

}

}

}

Output:

Thread-0 Hello..

Thread-0 Nilton

Thread-3 Hello..

Thread-3 Jack

Thread-2 Hello..

Thread-2 Amit

Thread-1 Hello..

Thread-1 Ankit

.

8.Inter-Thread Communication

.

It is possible for synchronized threads to communicate with each other regarding the lock status of a shared resource. One thread can pause its execution in a synchronized section and allow another thread to execute, and then resume the first thread.

This can be achieved using the following methods:

 wait() : It causes the calling thread to release the lock and pause its execution until another thread calls notify() or notifyAll() on the same object.
 notify() : It wakes up a single thread that had called wait() on the same object.
 notifyAll() : It wakes up all the threads that had called wait() on the same object.

Example:

In the following example, we have a one thread sets the username variable and the other thread prints it. The printUserThread waits until the AddUserThreads set the value and calls notify, and then it prints it.

public class HelloExample {

private String username;

   public synchronized void print() {
try {
if(username == null){
System.out.println(Thread.currentThread().getName() + ” No username

                                                        found..waiting..”);
wait();

System.out.println(Thread.currentThread().getName() + ” Hello..” +

                                                                     username);
}

}
catch (InterruptedException e) {
e.printStackTrace();

}

  }

  public synchronized void add(String name) {
username = name;
System.out.println(Thread.currentThread().getName() + ” User added.

                                                                 notifying..”);
notify();

  }

}

.

public class AddUserThread extends Thread {

HelloExample helloExample;
String name;

AddUserThread(HelloExample helloExample, String name)
{

this.helloExample = helloExample;
this.name = name;
}


@Override
public void run() {
helloExample.add(name);
}

}

.

public class PrintUserThread extends Thread {

HelloExample helloExample;

PrintUserThread(HelloExample helloExample)
{

this.helloExample = helloExample;
}


@Override
public void run() {
helloExample.print();
}

}

.

public class TestThreadExample {
public static void main(String[] args) {
       
HelloExample helloExample = new HelloExample();

PrintUserThread printUserThread = new PrintUserThread(helloExample);
printUserThread.start();

AddUserThread addUserThread = new AddUserThread(helloExample,

                                                                     “Nilton”);
addUserThread.start();

}
}

.

Output:

Thread-0 No username found..waiting..

Thread-1 User added. notifying..

Thread-0 Hello..Nilton

.

9.ExecutorService

.

public interface ExecutorService extends Executor

.

 The ExecutorService interface, defined in java.util.concurrent package, helps in asynchronous execution of tasks on large number of threads.
 The Executor maintains a thread pool and is responsible for assigning the tasks to threads from the pool and executing them.
 The implementation of ExecutorService can execute tasks which implement Runnable or Callable interface.
 The Callable interface returns a ‘Future’ object that can be used to get the status and result of the task.

Creating an ExecutorService:

ExecutorService executorService = Executors.newFixedThreadPool(3); //creates a pool of 3 threads

ExecutorService executorService = Executors.newSingleThreadExecutor() //creates ExecutorService with 1 thread

.

ExecutorService executorService = Executors. newScheduledThreadPool(3);//creates a pool of 3 threads with scheduling option

.

Example:

Following is an example that shows the simplest implementation of ExecutorService:

public class ExecutorExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {

ExecutorService executorService = Executors.newFixedThreadPool(3);

Runnable runnableTask = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() +

                                         (runnable) ” + LocalDateTime.now());
}
catch (InterruptedException e) {
e.printStackTrace();

}

}

};


Callable<String> callableTask = new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName() + ” (callable)

                                                    + LocalDateTime.now());
return “callable executed!”;
}

};


//submit Runnable task
executorService.submit(runnableTask);

//submit callable task
Future<String> future = executorService.submit(callableTask);
System.out.println(“callable done? ” + future.isDone());
System.out.println(“callable result: ” + future.get());

executorService.shutdown();
}

}

.

Output:

callable done? false

pool-1-thread-2 (callable) 2021-05-30T22:09:27.375807900

callable result: callable executed!

pool-1-thread-1 (runnable) 2021-05-30T22:09:29.313810300

.

Leave a Reply

Your email address will not be published. Required fields are marked *

Working Hours

  • Monday 9am - 6pm
  • Tuesday 9am - 6pm
  • Wednesday 9am - 6pm
  • Thursday 9am - 6pm
  • Friday 9am - 6pm
  • Saturday Closed
  • Sunday Closed