Multithreading in Java Tutorial
In this post, we feature a comprehensive Multithreading in Java Tutorial. Multithreading is the ability of a program to manage its use by more than one user and even to manage multiple requests by the same user. In the Java programming language, concurrent programming is the execution of processes and threads. Java has supported java.lang.Thread
since JDK 1.0. The java.util.concurrent
API is released in Java 5. Java collections – Vector
, Stack
and HashTable
are thread-safe. Java provides synchronized functionality to support thread-safety on collections such as Collections.SynchronizedCollection()
and Collections.SynchronizedMap()
.
You can also check this tutorial in the following video:
Table Of Contents
1. Process and Thread
A process is an execution of a program and a thread is a single execution of work within the process. A process can contain multiple threads. A thread is also known as a lightweight process.
In Java, a process is run independently from other processes in a JVM and threads in a JVM share the heap belonging to that process. That is why several threads may access the same object in the heap. Threads share the heap and have their own stack space. Therefore, an invocation of a method and its local variables are kept thread safe from other threads, while heap is not thread safe and must be synchronized for multithreaded programs.
2. Life Cycle of a Thread
A thread can be in different states in its life cycle. The diagram below displays different states of a thread which are start, run, sleep/wait/block and done.
- New: When a thread is created, it is in the new state.
- Runnable: A thread is waiting for its turn to be picked for execution. The thread is selected by the thread scheduler based on thread priorities. The
start()
methods registers a thread in a thread scheduler. - Running: The processor is executing the thread. The thread runs until it becomes blocked or give up its turn with
Thread.yield()
. Due to overhead of context switching,yield()
should not be used very frequently. Thewait()
,sleep()
, andjoin()
methods make thread leave the running state. - Waiting: A thread waits for another thread to perform a task.
- Sleeping: Java threads are forced to sleep (suspended) with this overloaded method:
Thread.sleep(milliseconds)
,Thread.sleep(milliseconds, nanoseconds)
. - Blocked on I/O: A thread is blocked on some external I/O processing to finish. Thread will move to Runnable after I/O condition like reading bytes of data etc changes.
- Blocked on synchronization: Thread will move to Runnable when a lock is acquired.
- Terminated: The thread is finished its work.
3. Technologies Used
The example code in this article was built and run using:
- Java 11
- Maven 3.3.9
- Eclipse Oxygen
- Logback 1.2.3
- Junit 4.12
4. Maven Project
In this step, I will create a Maven project to demonstrate multi-threading in Java.
4.1 Dependencies
Add Logback
and Junit
libraries to the pom.xml
.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>java-multithreads-demo</groupId> <artifactId>java-multithreads-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <release>11</release> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-access</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.3</version> </dependency> </dependencies> </project>
4.2 CommonUtil
In this step, I will create a CommonUtil
class which holds the constants – THREAD_STARTED
, THREAD_FINISHED
– and THREAD_STATE
and two methods – fiveSecondsProcess()
and waitforThread()
.
CommonUtil.java
package jcg.zheng.multithread.demo; import java.util.concurrent.TimeUnit; public class CommonUtil { public static final String THREAD_FINISHED = " Thread finished"; public static final String THREAD_STARTED = " Thread started"; public static final String THREAD_STATE = "Thread state = "; public static void fiveSecondsProcess() { try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } public static void waitForThread(Thread thread) { try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } }
5. Create a Thread
Java Thread has nine constructors to create a new thread. We categorize into two ways:
- Extends
Thread
class - Implements
Runnable
interface
5.1 Extends Thread Class
The Thread
class itself implements the Runnable
interface. When a class extends Thread
, it should override the run()
method and provide its own implementation of run()
.
The start()
method in the Thread
class starts the execution of a thread. The thread will be alive until the execution of the run method is finished.
In this step, I will create a ThreadExample
class which extends from Thread
and have two run
methods:
run()
– annotates with@Override
, it will be called by the Thread’sstart()
method.run(String msg)
– it is a normal class method. It will be called when invoking it.
ThreadExample.java
package jcg.zheng.multithread.demo.thread; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jcg.zheng.multithread.demo.CommonUtil; public class ThreadExample extends Thread { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public void run() { logger.info(CommonUtil.THREAD_STARTED); CommonUtil.fiveSecondsProcess(); logger.info(CommonUtil.THREAD_FINISHED); } public void run(String msg) { logger.info(" ** " + msg); } public static void main(String[] args) { System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_STARTED); ThreadExample thread = new ThreadExample(); thread.run("Mary : Hello !"); System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_STATE + thread.getState()); thread.start(); System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_STATE + thread.getState()); if (args.length == 1 && args[0].equalsIgnoreCase("wait")) { System.out.println("Wait!"); CommonUtil.waitForThread(thread); } System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_STATE + thread.getState()); System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_FINISHED); } }
Execute it as a Java application and capture the output here.
Output
C:\Users\aa00765\Desktop\Design_diagrams>java -jar TheadExample.jar wait main Thread started 17:12:12.040 [main] INFO jcg.zheng.multithread.demo.thread.ThreadExample - ** Mary : Hello ! mainThread state = NEW mainThread state = RUNNABLE Wait! 17:12:12.048 [Thread-0] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread started 17:12:17.051 [Thread-0] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread finished mainThread state = TERMINATED main Thread finished C:\Users\aa00765\Desktop\Design_diagrams>java -jar TheadExample.jar main Thread started 17:12:20.402 [main] INFO jcg.zheng.multithread.demo.thread.ThreadExample - ** Mary : Hello ! mainThread state = NEW mainThread state = RUNNABLE mainThread state = RUNNABLE 17:12:20.410 [Thread-0] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread started main Thread finished 17:12:25.416 [Thread-0] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread finished C:\Users\aa00765\Desktop\Design_diagrams>
5.2 Implements Runnable
Similar to the previous implementation of a thread, when a class implements Runnable
interface, it should provide its own implementation of run()
. Java 5 marks the Runnable
interface with @FunctionalInterface
, so we can use Java 8 lambda to create a new thread with a Runnable
.
In this step, I will create a RunnableThreadExample
class which implements the Runnable
interface.
RunnableThreadExample.java
package jcg.zheng.multithread.demo.thread; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jcg.zheng.multithread.demo.CommonUtil; public class RunnableThreadExample implements Runnable { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public void run() { logger.info(CommonUtil.THREAD_STARTED); CommonUtil.fiveSecondsProcess(); logger.info(CommonUtil.THREAD_FINISHED); } public static void main(String[] args) { System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_STARTED); Thread thread = new Thread(new RunnableThreadExample()); System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_STATE + thread.getState()); thread.start(); System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_STATE + thread.getState()); if (args.length == 1 && args[0].equalsIgnoreCase("wait")) { System.out.println("Wait!"); CommonUtil.waitForThread(thread); } System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_FINISHED); } }
Execute it as a Java application and capture the output here.
Output
main Thread started mainThread state = NEW mainThread state = RUNNABLE main Thread finished 17:15:08.822 [Thread-0] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread started 17:15:13.844 [Thread-0] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread finished main Thread started mainThread state = NEW mainThread state = RUNNABLE Wait! 17:15:41.740 [Thread-0] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread started 17:15:46.751 [Thread-0] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread finished main Thread finished
5.3 ThreadTestBase
In this step, I will create a ThreadTestBase
which test a thread’s creation, execution, and its state.
ThreadTestBase.java
package jcg.zheng.multithread.demo.thread; import static org.junit.Assert.assertEquals; import java.lang.Thread.State; import org.junit.Test; import jcg.zheng.multithread.demo.CommonUtil; public class ThreadTestBase { Thread thread; @Test public void create_start_finish() { assertEquals(State.NEW, thread.getState()); assertEquals(State.NEW, thread.getState()); thread.start(); assertEquals(State.RUNNABLE, thread.getState()); CommonUtil.waitForThread(thread); assertEquals(State.TERMINATED, thread.getState()); } @Test(expected = IllegalThreadStateException.class) public void throw_exception_start_twice() { thread.start(); thread.start(); } }
As you have seen in the create_start_finish
method, the newly created thread is in the NEW
state, then changes to RUNNABLE
by the start
method, finally, it’s in TERMINATED
after it’s completed.
As you have seen in the throw_exception_start_twice
method, it throws an IllegalThreadStateExecption
when invoking start()
twice.
5.4 ThreadExampleTest
In this step, I will create a ThreadExampleTest
. At the setup method, it creates a Thread
instance from ThreadExample
.
ThreadExampleTest.java
package jcg.zheng.multithread.demo.thread; import static org.junit.Assert.assertEquals; import java.lang.Thread.State; import org.junit.Before; public class ThreadExampleTest extends ThreadTestBase{ @Before public void setup() { thread = new ThreadExample(); assertEquals(State.NEW, thread.getState()); ((ThreadExample)thread).run("Mary : Hello !"); assertEquals(State.NEW, thread.getState()); } }
Execute mvn test -Dtest=ThreadExampleTest
and capture the output here.
Output
Running jcg.zheng.multithread.demo.thread.ThreadExampleTest 17:19:25.769 [main] INFO jcg.zheng.multithread.demo.thread.ThreadExample - ** Mary : Hello ! 17:19:25.781 [Thread-0] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread started 17:19:30.796 [Thread-0] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread finished 17:19:30.802 [main] INFO jcg.zheng.multithread.demo.thread.ThreadExample - ** Mary : Hello ! Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 5.38 sec 17:19:30.838 [Thread-1] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread started Results : Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 16.388 s [INFO] Finished at: 2019-08-12T17:19:31-05:00 [INFO] ------------------------------------------------------------------------ C:\MaryZheng\Workspaces\jdk12\java-multithreads-demo>
5.5 RunnableThreadExampleTest
In this step, I will a RunnableThreadExampleTest
with two methods:
setup
– create aThread
instance fromRunnableThreadExample
createWithLambda
– create aThread
instance with Java 8 Lamdba syntax
RunnableThreadExampleTest.java
package jcg.zheng.multithread.demo.thread; import static org.junit.Assert.assertEquals; import java.lang.Thread.State; import org.junit.Before; import org.junit.Test; public class RunnableThreadExampleTest extends ThreadTestBase { @Before public void setup() { thread = new Thread(new RunnableThreadExample()); } @Test public void createWithLambda() { thread = new Thread(() -> { System.out.println(("Hello from Lambda!")); }); assertEquals(State.NEW, thread.getState()); thread.start(); assertEquals(State.RUNNABLE, thread.getState()); } }
Execute mvn test -Dtest=RunnableThreadExampleTest
and capture the output here.
Output
Running jcg.zheng.multithread.demo.thread.RunnableThreadExampleTest Hello from Lambda! 17:22:51.231 [Thread-2] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread started 17:22:56.238 [Thread-2] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread finished Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 5.437 sec Results : Tests run: 3, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 13.272 s [INFO] Finished at: 2019-08-12T17:22:56-05:00 [INFO] ------------------------------------------------------------------------ C:\MaryZheng\Workspaces\jdk12\java-multithreads-demo>
6. Thread Details
6.1 Thread Priority
Every thread has a priority that helps the program to determine the order which threads are scheduled. Thread priority is between MIN_PRIORITY
(a constant of 1) and MAX_PRIORITY
(a constant of 10). The default priority of a thread is NORM_PRIORITY
(a constant of 5). Threads with higher priority are executed before threads with lower priority. However, thread priorities do not guarantee the order in which threads execute next and it is also platform dependent.
ThreadPriorityExample.java
package jcg.zheng.multithread.demo.thread; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jcg.zheng.multithread.demo.CommonUtil; public class ThreadPriorityExample extends Thread { private Logger logger = LoggerFactory.getLogger(this.getClass()); public void run() { logger.info(CommonUtil.THREAD_STARTED); logger.info("Running Thread Priority: " + Thread.currentThread().getPriority()); CommonUtil.fiveSecondsProcess(); logger.info(CommonUtil.THREAD_FINISHED); } public static void main(String[] args) { System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_STARTED); /* Thread Priority */ ThreadPriorityExample minThread = new ThreadPriorityExample(); minThread.setName("Thread1_MIN_PRIORITY"); minThread.setPriority(Thread.MIN_PRIORITY); ThreadPriorityExample maxThread = new ThreadPriorityExample(); maxThread.setName("Thread2_MAX_PRIORITY"); maxThread.setPriority(Thread.MAX_PRIORITY); ThreadPriorityExample thread3 = new ThreadPriorityExample(); thread3.setName("Thread3"); System.out.println(Thread.currentThread().getName() + " starts with min, max, default priority order"); minThread.start(); maxThread.start(); thread3.start(); System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_FINISHED); } }
Execute it as a Java application and capture the output here.
Output
main Thread started main starts with min, max, default priority order main Thread finished 17:24:59.573 [Thread2_MAX_PRIORITY] INFO jcg.zheng.multithread.demo.thread.ThreadPriorityExample - Thread started 17:24:59.580 [Thread2_MAX_PRIORITY] INFO jcg.zheng.multithread.demo.thread.ThreadPriorityExample - Running Thread Priority: 10 17:24:59.573 [Thread3] INFO jcg.zheng.multithread.demo.thread.ThreadPriorityExample - Thread started 17:24:59.593 [Thread3] INFO jcg.zheng.multithread.demo.thread.ThreadPriorityExample - Running Thread Priority: 5 17:24:59.573 [Thread1_MIN_PRIORITY] INFO jcg.zheng.multithread.demo.thread.ThreadPriorityExample - Thread started 17:24:59.594 [Thread1_MIN_PRIORITY] INFO jcg.zheng.multithread.demo.thread.ThreadPriorityExample - Running Thread Priority: 1 17:25:04.584 [Thread2_MAX_PRIORITY] INFO jcg.zheng.multithread.demo.thread.ThreadPriorityExample - Thread finished 17:25:04.594 [Thread3] INFO jcg.zheng.multithread.demo.thread.ThreadPriorityExample - Thread finished 17:25:04.594 [Thread1_MIN_PRIORITY] INFO jcg.zheng.multithread.demo.thread.ThreadPriorityExample - Thread finished
6.2 Thread Factory
The ThreadFactory Interface defines a newThread(Runnable r)
method to create a thread on demand.
In this step, I will create a Thread
from ThreadFactory
.
ThreadFactoryExample.java
package jcg.zheng.multithread.demo.thread; import java.util.concurrent.ThreadFactory; import jcg.zheng.multithread.demo.CommonUtil; public class ThreadFactoryExample implements ThreadFactory { public static void main(String[] args) { System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_STARTED); ThreadFactoryExample tFactory = new ThreadFactoryExample("MZhengThreadFactory"); for (int i = 0; i < 5; i++) { Thread thread = tFactory.newThread(new ThreadExample()); thread.start(); } System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_FINISHED); } private int threadId; private String threadName; public ThreadFactoryExample(String name) { threadId = 1; this.threadName = name; } @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r, threadName + "-Thread_" + threadId); threadId++; return thread; } }
Execute it as a Java application and capture the output here.
Output
main Thread started main Thread finished 17:26:52.681 [MZhengThreadFactory-Thread_2] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread started 17:26:52.684 [MZhengThreadFactory-Thread_3] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread started 17:26:52.685 [MZhengThreadFactory-Thread_1] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread started 17:26:52.681 [MZhengThreadFactory-Thread_5] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread started 17:26:52.684 [MZhengThreadFactory-Thread_4] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread started 17:26:57.724 [MZhengThreadFactory-Thread_1] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread finished 17:26:57.724 [MZhengThreadFactory-Thread_2] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread finished 17:26:57.724 [MZhengThreadFactory-Thread_3] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread finished 17:26:57.725 [MZhengThreadFactory-Thread_4] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread finished 17:26:57.725 [MZhengThreadFactory-Thread_5] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread finished
6.3 Executor Service
ExecutorService provides an indirect way to create a thread. It manages a thread pool, all threads of the internal pool will be reused.
In this step, I will demonstrate how to get a thread from ExecutorService
.
ExecutorServiceExample.java
package jcg.zheng.multithread.demo.thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import jcg.zheng.multithread.demo.CommonUtil; public class ExecutorServiceExample { public static void main(String[] args) { System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_STARTED); ExecutorService service = Executors.newFixedThreadPool(5); for (int i = 0; i < 5; i++) { service.submit(new RunnableThreadExample()); } service.submit(() -> { System.out.println(Thread.currentThread().getName() + " is reused"); }); try { // Executor must be stopped explicitly otherwise it keeps listens for new // tasks service.shutdown(); service.awaitTermination(10l, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println(Thread.currentThread().getName() + " isTerminated = " + service.isTerminated()); service.shutdownNow(); } System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_FINISHED); } }
Execute it as a Java application and capture the output here.
Output
main Thread started 17:28:15.344 [pool-1-thread-2] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread started 17:28:15.345 [pool-1-thread-1] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread started 17:28:15.344 [pool-1-thread-4] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread started 17:28:15.345 [pool-1-thread-3] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread started 17:28:15.344 [pool-1-thread-5] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread started 17:28:20.357 [pool-1-thread-2] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread finished 17:28:20.357 [pool-1-thread-3] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread finished 17:28:20.357 [pool-1-thread-5] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread finished 17:28:20.357 [pool-1-thread-1] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread finished 17:28:20.357 [pool-1-thread-4] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread finished pool-1-thread-4 is reused main isTerminated = true main Thread finished
As you have seen in this example, it created a thread-pool with five threads. The thread-4
is reused for the sixth request.
6.4 Thread Notify and Wait
In this step, I will invoke the notify()
and wait()
methods to wake up and pause the current thread.
First, I will create two synchronized methods:
printEven
– it notifies the current thread if the number is even and pauses the current thread if the number is odd.printOdd
– it notifies the current thread if the number is odd and pauses the current thread if the number is even.
Second, I will create a ThreadPrintOdd
class which extends from Thread and invokes the printOdd()
in the run()
method.
Third, I will create a Runnable
interface via Java 8 lambda syntax which invokes printEven()
method.
Finally, I will create a main
application which creates oddThread
and evenThreadJdk8
and starts both thread. As the output shows, these two threads will take a turn to pause and execute.
ThreadNotifyWaitExample.java
package jcg.zheng.multithread.demo.thread; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jcg.zheng.multithread.demo.CommonUtil; public class ThreadNotifyWaitExample extends Thread { private static final class ThreadPrintOdd extends Thread { private final ThreadNotifyWaitExample tExample; private ThreadPrintOdd(ThreadNotifyWaitExample tExample) { this.tExample = tExample; } @Override public void run() { try { tExample.printOdd(); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_STARTED); ThreadNotifyWaitExample tExample = new ThreadNotifyWaitExample(); Thread oddThread = new ThreadPrintOdd(tExample); Thread evenThreadJdk8 = new Thread(printEven(tExample)); oddThread.start(); evenThreadJdk8.start(); System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_FINISHED); } private static Runnable printEven(ThreadNotifyWaitExample tExample) { return () -> { try { tExample.printEven(); } catch (InterruptedException e) { e.printStackTrace(); } }; } private Logger logger = LoggerFactory.getLogger(this.getClass()); public synchronized void printEven() throws InterruptedException { for (int i = 0; i < 10; i++) { if (i % 2 == 0) { this.notify(); logger.info("Even: " + i); } else { this.wait(); } } } public synchronized void printOdd() throws InterruptedException { for (int i = 0; i < 10; i++) { if (i % 2 == 0) { this.wait(); } else { this.notify(); logger.info("Odd: " + i); } } } }
Execute it as a Java application and capture the output here.
Output
main Thread started main Thread finished 17:29:19.232 [Thread-2] INFO jcg.zheng.multithread.demo.thread.ThreadNotifyWaitExample - Even: 0 17:29:19.241 [Thread-1] INFO jcg.zheng.multithread.demo.thread.ThreadNotifyWaitExample - Odd: 1 17:29:19.242 [Thread-2] INFO jcg.zheng.multithread.demo.thread.ThreadNotifyWaitExample - Even: 2 17:29:19.242 [Thread-1] INFO jcg.zheng.multithread.demo.thread.ThreadNotifyWaitExample - Odd: 3 17:29:19.242 [Thread-2] INFO jcg.zheng.multithread.demo.thread.ThreadNotifyWaitExample - Even: 4 17:29:19.242 [Thread-1] INFO jcg.zheng.multithread.demo.thread.ThreadNotifyWaitExample - Odd: 5 17:29:19.242 [Thread-2] INFO jcg.zheng.multithread.demo.thread.ThreadNotifyWaitExample - Even: 6 17:29:19.242 [Thread-1] INFO jcg.zheng.multithread.demo.thread.ThreadNotifyWaitExample - Odd: 7 17:29:19.242 [Thread-2] INFO jcg.zheng.multithread.demo.thread.ThreadNotifyWaitExample - Even: 8 17:29:19.242 [Thread-1] INFO jcg.zheng.multithread.demo.thread.ThreadNotifyWaitExample - Odd: 9
7. Multithreading in Java
In the step, I will create a multithreading application. It has three threads:
thread1
– instance ofRunnableTheadExample
thread2
– instance ofRunnableThreadExample
thread3
– instance ofThreadExample
After created three threads, thread1
and thread2
are started, then,CommonUtil.waitForThread(thread1)
is called to pause the current until thread1
is finished. After both thread1
and thread2
completes, thread3
will be executed. Finally, the main
thread exits before thread3
completes.
MultithreadsApp.java
package jcg.zheng.multithread.demo.thread; import jcg.zheng.multithread.demo.CommonUtil; public class MultiThreadsApp extends Thread { public static void main(String[] args) { System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_STARTED); Thread thread1 = new Thread(new RunnableThreadExample()); Thread thread2 = new Thread(new RunnableThreadExample()); ThreadExample thread3 = new ThreadExample(); thread1.start(); thread2.start(); CommonUtil.waitForThread(thread1); CommonUtil.waitForThread(thread2); thread3.start(); System.out.println(Thread.currentThread().getName() + CommonUtil.THREAD_FINISHED); } }
Execute it as a Java application and capture the output here.
Output
main Thread started 18:12:56.367 [Thread-1] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread started 18:12:56.367 [Thread-0] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread started 18:13:01.384 [Thread-1] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread finished 18:13:01.384 [Thread-0] INFO jcg.zheng.multithread.demo.thread.RunnableThreadExample - Thread finished main Thread finished 18:13:01.386 [Thread-2] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread started 18:13:06.386 [Thread-2] INFO jcg.zheng.multithread.demo.thread.ThreadExample - Thread finished
8. Synchronization
Synchronization is about to control the access of threads on shared resources in the program. In Java, each object has a lock. A thread can acquire the lock for an object by using synchronized
keyword.
The synchronized keyword can be implemented in method level or block level. The block level is more efficient in comparison to method level because it does not lock the whole method.
8.1 Race Condition
In this step, I will create a simple Java application to demonstrate the race condition in a concurrent multithreading application.
The RaceConditionExample
class has a mutable member – count
. In the raceConditionDemo
method, It will create a thread-pool with 5 threads and submit 10000 times. So it should print out 10000.
As you will see in the output, it prints as 9987 due to race condition. You will get unpredictable number which closes to 10000 for each execution.
RaceConditionExample.java
package jcg.zheng.multithread.demo.thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; public class RaceConditionExample { private int count = 0; public int getCount() { return count; } AtomicInteger atomicCount = new AtomicInteger(); public int getCountViaAtomicInteger() { count = atomicCount.incrementAndGet(); return count; } public void increment() { count++; } public synchronized void incrementSyncMethod() { count++; } public void incrementSyncBlock() { synchronized (this) { count++; } } // mutual exclusion, same as synchronized ReentrantLock lock = new ReentrantLock(); public void incrementLock() { lock.lock(); try { count++; } finally { lock.unlock(); } } public void raceConditionDemo(String type) { ExecutorService executor = Executors.newFixedThreadPool(5); for (int i = 0; i < 10000; i++) { switch (type) { case "RaceCondition": executor.submit(this::increment); break; case "Lock": executor.submit(this::incrementLock); break; case "SynchronizedBlock": executor.submit(this::incrementSyncBlock); break; case "SynchronizedMethod": executor.submit(this::incrementSyncMethod); break; case "AtomicInteger": executor.submit(this::getCountViaAtomicInteger); break; } } try { executor.awaitTermination(10l, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } executor.shutdown(); } public static void main(String[] args) { RaceConditionExample rE = new RaceConditionExample(); rE.raceConditionDemo(args[0]); System.out.println("Count = " + rE.getCount()); } }
Execute it as a Java application and capture the output here.
Output
C:\MaryZheng\Workspaces\jdk12\java-multithreads-demo\target\classes>java jcg.zheng.multithread.demo.thread.RaceConditionExample RaceCondition Count = 9987 C:\MaryZheng\Workspaces\jdk12\java-multithreads-demo\target\classes>
8.2 Synchronized Block
When multiple threads access the same mutable objects, we should synchronize the mutable object to avoid the incorrect data due to race condition error.
Please reference the incrementSyncBlock
method.
Execute it as a Java application and capture the output here.
Output
C:\MaryZheng\Workspaces\jdk12\java-multithreads-demo\target\classes>java jcg.zheng.multithread.demo.thread.RaceConditionExample SynchronizedBlock Count = 10000 C:\MaryZheng\Workspaces\jdk12\java-multithreads-demo\target\classes>
8.3 Synchronized Method
The xx method is marked with synchronized
keyword. I will demonstrate how to use it to avoid the race condition
Please reference the incrementSyncMethod
method
Execute it as a Java application and capture the output here.
Output
C:\MaryZheng\Workspaces\jdk12\java-multithreads-demo\target\classes>java jcg.zheng.multithread.demo.thread.RaceConditionExample SynchronizedMethod Count = 10000 C:\MaryZheng\Workspaces\jdk12\java-multithreads-demo\target\classes>
8.4 Lock
In this step, I will demonstrate how to use ReentrantLock
to avoid the race condition.
Execute it as a Java application and capture the output here.
Output
C:\MaryZheng\Workspaces\jdk12\java-multithreads-demo\target\classes>java jcg.zheng.multithread.demo.thread.RaceConditionExample Lock Count = 10000 C:\MaryZheng\Workspaces\jdk12\java-multithreads-demo\target\classes>
8.5 AtomicInteger
In this step, I will demonstrate how to use the AtomicInteger class to avoid race condition.
Execute it as a Java application and capture the output here.
Output
C:\MaryZheng\Workspaces\jdk12\java-multithreads-demo\target\classes>java jcg.zheng.multithread.demo.thread.RaceConditionExample AtomicInteger Count = 10000 C:\MaryZheng\Workspaces\jdk12\java-multithreads-demo\target\classes>
9. Summary
In this tutorial, I demonstrated thread creation, thread state, thread priority, thread factory, thread pool, and synchronization. The java.util.concurrent
API contains high-level utilities that are mainly used in concurrent programming. Here are the pros and cons of Multi-threading.
Pros:
- Better use of system resources
- Parallel execution of tasks and thus less execution time
- Enhanced performance on multi-processor machines
- Improved GUI responsiveness
- Independent threads (don’t impact other threads of the same process if an exception occurs)
Cons:
- Complexity of the code
- Synchronization of shared resources (objects, data) is CPU/memory intensive
- Debugging is hard because sometimes you can’t predict the results
- Increased potential for deadlock occurrence
- “Starvation” some of the threads may not be served with due to poor design
10. Download the Source Code
This was a tutorial for Java multithreading tutorial.
You can download the full source code of this example here: Multithreading in Java Tutorial
Multithreading in Java Tutorial was last updated on Aug 14th, 2019
Nice post!
After understanding the basics of Java multithreading, I recommend to check the Executors framework. Together with various built in classes, making complex multithreaded applications is simpler and cleaner than using Threads directly.
Can you please help me to find out the answer of the below question using threads??
The O/P should become
ODD : 1
EVEN : 2
EVEN : 4
ODD : 3
. .
ODD : 9
ODD : 11
ODD : 13
EVEN : 6
. .
EVEN : 20
ODD : 15
. .
ODD : 19
public class ThreadEvenOdd extends Thread{ public ThreadEvenOdd() { } public synchronized void printOdd() throws InterruptedException{ for(int i=1;i<10;i=i+1){ if(i%2==0){ this.wait(); } else{ this.notify(); System.out.println("odd"+" " +i); } } } public synchronized void printEven() throws InterruptedException{ for(int i=2;i<10;i=i+1){ if(i%2==0){ this.notify(); System.out.println("even"+" "+i); } else{ this.wait(); } } } public static void main(String[] args) { ThreadEvenOdd even=new ThreadEvenOdd(); Thread thread=new Thread() { @Override public void run() { try { even.printOdd(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; Thread thread1=new Thread() { @Override public void run() { try { even.printEven(); } catch (InterruptedException e) { // TODO… Read more »
I’m doing core java programming language I/O streaming, swing and AWT,jDBC, RMI, javanetwork programming, servlets, JSP shell and python programming any body who can help me
thanks for this great information on multi threading in java. I have found this really helpful..
Keep Posting here Multithreading In Java
Java provides multithreading support with the Thread class and an application can create multiple threads executing concurrently. Thanks for sharing this information.
Thanks for this great information on multi threading in java.
thanks for the information