Do you think IIT Guwahati certified course can help you in your career?
No
🌟Introduction
Multithreading in spring boot is similar to multitasking, except that it allows numerous threads to run concurrently rather than processes. A thread in Java is a sequence of programmed instructions that are managed by the operating system's scheduler. Threads can be utilized in the background to conduct complex activities without interfering with the main program. It is worth mentioning that a thread shares common memory with other threads across the application, but a process is separate and does not share memory.
⚡Applications of Multithreading in Spring Boot
👉 To save time by completing numerous actions at once.
👉 To avoid blocking the user(s) while one operation is executed.
👉 If an exception occurs in one thread, other threads are unaffected.
👉 CPU use can be maximized by running two or more threads concurrently.
👉 Breaking up larger issues into smaller, faster computations helps reduce reaction time.
🌟Thread States
The getState() method can be used to determine the current status of a Thread. The Thread.State describes the various states of a Thread.
👉 NEW - a newly created thread instance that has not yet begun.
👉 RUNNABLE - a thread that is active. It is named runnable because it can be executing or waiting for the next time interval from the scheduler at any given time. When the Thread.start() method is called on a thread, it changes from NEW to RUNNABLE.
👉 BLOCKED - A running thread becomes BLOCKED if it needs to enter a synchronized segment but is unable to do so because another thread is holding the monitor of that section.
👉 WAITING - A thread enters this state while it is waiting for another thread to complete a task. For example, if the Object.wait() function on the monitor or a Thread.join() method on another thread is called.
👉 TIMED WAITING - This state is identical to WAITING, but a thread enters this state after invoking timed versions of Thread.join(), Thread.sleep(), or Object.wait(),.
👉 TERMINATED - a thread has terminated after completing the execution of its Runnable.run() method.
🌟Synchronized Methods
The synchronized keyword preceding a block indicates that any thread entering this block must obtain the monitor. If another thread has already obtained the monitor, the former thread will enter the BLOCKED state and wait for the monitor to be released. The monitor for a synchronized method can vary depending on the method style:
👉 The parameter is used as the monitor by a simple synchronized block within a method.
synchronized(object) {…}
👉 The instance itself serves as the monitor in a synchronized instance method.
synchronized void instanceMethod() {…}
👉 The Class object serves as the monitor in a static synchronized method.
static synchronized void staticMethod() {…}
🌟General Java Multithreading
You can run as many threads as your CPU cores allow at any given time (s). However, before deciding how many threads to make available in your application, you should analyze how your code works:
For CPU-intensive operations, threads should be limited to no more than the number of cores in the system; if other programs use threads, this number may need to be reduced.
For network or I/O intensive jobs, we may choose to have more threads than cores available if threads are performing HTTP calls and waiting for a response, or if slow reads/writes to the disc.
⚡Dual Threaded Applications
An ExecutorService produced by the Executors.newSingleThreadExecutor() static method can be used to create a single additional thread. The ExecutorService includes the execute() method, which accepts any class implementing the Runnable interface or a lambda expression as an input.
An Executor can be used to start the thread at any point throughout the application's execution. Loading cached data as a Spring application boots up is one example. To accomplish this, the Executor call would be made in the Spring Boot application's main method, prior to the SpringApplication.run(). Dual threading is also handy for doing a set of actions that must be completed in a specific order while also desiring to perform another activity.
Multithreaded Java Applications
⚡Multithreaded Java Applications
There are three typical implementations of the ExecutorService interface:
ThreadPoolExecutor - utilized for task execution using a thread pool. When a thread finishes executing a job, it returns to the pool. If all of the threads in the pool are busy, the task must wait for its turn.
ScheduledThreadPoolExecutor - permits task execution to be scheduled rather than doing it immediately when a thread becomes available. It can also schedule tasks at a set rate or with a delay.
ForkJoinPool - a specialized ExecutorService for recursive algorithm jobs. If you use a standard ThreadPoolExecutor for a recursive algorithm, you'll see that all of the threads are busy waiting for the lower levels of recursion to complete. ForkJoinPool employs a "work-stealing" mechanism that allows it to make better use of available threads.
📣Thread Pools
Thread Pools come into play when more than two threads need to be used at the same time. Thread Pools are made up of one or more threads, and a queue, where tasks are held until a thread is ready to complete them. A Thread Pool's primary attributes are as follows:
Core Pool Size - The number of threads that are always present in the core pool.
Max Pool Size - the most threads that can be produced.
Keep Alive Time - the amount of time idle threads are permitted to exist.
Allow Core Thread Timeout - Normally false, but when set to true, core threads can be destroyed after the keep-alive timer expires.
In Java, there are several types of thread pools that can be used.
📣Fixed Thread Pool
The core and maximum pool sizes are equal, implying that the pool always has the same the number of threads.
A new thread is started whenever one thread fails to owe to an exception.
Threads continue to exist until the .shutdown() method is called.
When running a program with more tasks than the maximum pool size, one of the threads must finish processing before it can handle the subsequent tasks.
📣Cached Thread Pool
A "limitless" number of threads can be created.
The core pool size is zero, while the maximum pool size is Integer.MAX_VALUE
If previously generated threads are available, they will be used; otherwise, a new thread will be created and added to the pool.
Threads that have been idle for sixty seconds are killed and removed from the cache.
📣Scheduled Thread Pool
Have a fixed number of threads that can run tasks at regular time intervals.
The maximum pool size is Integer.MAX_VALUE.
Tasks can be completed in a variety of ways using the ScheduledExecutorService:
.schedule() - executes a single task after a specified delay.
.scheduleAtFixedRate() - Schedules a task to repeat; provided a time unit, a fixed rate period 'd,' and a delay. The job will run 'd' time units after the preceding execution's start time. If a job takes more time than the delay interval, the next execution will begin late but will not run concurrently.
.scheduleWithFixedDelay() - similar to a fixed rate, except the runnable will execute 'd' time units after the preceding task's end time.
🌟Multithreading in Spring Boot
⚡Async Configuration
To run methods in a different thread, use the Spring Async annotation (@Async). To configure @Async, a configuration class must be used to describe the thread pool that all these methods will use. The annotation @EnableAsync must be present in this configuration class. Spring defines the Thread Pool setup using a ThreadPoolTaskExecutor, for example:
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name="taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setQueueCapacity(100);
executor.setMaxPoolSize(2);
executor.setCorePoolSize(2);
executor.setThreadNamePrefix("poolThread-");
executor.initialize();
return executor;
}
}
You can also try this code with Online Java Compiler
Because the Core Pool Size and Max Pool Size are both equal in the previous example, the thread pool will always have two threads and a queue of 100. To verify the status of the thread pool, this bean can be accessed programmatically by Autowiring in the Executor bean and accessing this instance in the service.
⚡ThreadPoolTaskExecutor
Even if the core pool size is specified, there will be no threads in the pool at the start of the process with ThreadPoolTaskExecutor. Every time a job is completed, a new thread is launched until the core pool size is reached. When the core pool size is reached, the next job will be moved to the queue and will wait for a free thread. If the load is too high and the queue Capacity is full, new executor threads are created until the maximum pool size is reached; these extra threads die when the queue is empty. If the job's core pool size, queue capacity, or maximum pool size are all exceeded, the task is refused, and an exception is thrown.
⚡General Multithreading in Spring Boot
It's worth mentioning that you can't call an Async function from the same class in Spring. They are frequently invoked using a Rest controller or an Async Service class. If a service defines many Async methods but just one AsyncConfig, the thread pool is shared across all Async methods.
🌟Concurrency and Other Pitfalls
⚡Thread-Safe Objects
In general, threads have access to the same object. This might cause problems because as one thread reads an item, another may update it, resulting in a different result than intended for the reading thread. In the worst-case situation, continuously updating an object may result in it becoming corrupted or inconsistent.
Using immutable objects is the simplest way to prevent this problem. An immutable object's state cannot be changed by multiple threads. When this strategy is not possible, we must instead make our mutable objects thread-safe.
⚡Thread-Safe Collections
The internal state is maintained through collections (as all objects do). Multiple threads accessing a single collection object at the same time can all change this state. One possible solution is to synchronize the collection.
We ensure that only one thread can access these collections at a time by synchronizing them. As a result, the object is not left in an inconsistent state.
⚡Multithreaded Collections
Using a synchronized technique may cause additional problems. While the strategy is effective for blocking repeated updates to the state, we may have performance concerns if the threads are utilized to read the object. This is due to the fact that if two threads wish to read the same item, one must wait until the other has finished. Concurrent collections in Java, such as ConcurrentHashMap, can help with this.
Map<String, String> map = new ConcurrentHashMap<>();
You can also try this code with Online Java Compiler
ConcurrentHashMap is thread-safe and performs better than Collections.synchronizedMap() method. It is a thread-safe map constructed from thread-safe maps. Within the map, there are divisions that can be locked independently. This enables several actions to occur concurrently in the child maps.
⚡Using Non-Thread-Safe Objects
Sometimes, we have to work with objects that have no thread-safe equivalent. When this happens, it's advisable to employ a method that attempts to safeguard the object's state from other threads. Here are some examples of these approaches:
Create a new instance of the object each time you want to utilize it.
To restrict object access, use the Synchronize keyword.
ThreadLocal<Object> should be used to generate an instance of Object for each thread.
🌟Memory Consistency
Memory Consistency refers to the issue that occurs when various threads have different values for the same item. This is possible because most current computers have a hierarchy of caches on top of their core memory. A variable can be stored in any of these caches by any thread. The following is an example of how this could cause problems:
The same item is accessed by two threads.
Both threads save the object's value in separate caches.
The first thread modifies the object's value.
The second thread retrieves the original value from a different cache.
There is no guarantee that the predicted behavior of reading the correct value will occur.
As previously stated, this can be solved with synchronization, perhaps at the loss of performance. Another option is to use the volatile keyword. We use volatile to ensure that any change to a variable is visible to all threads. It is important to note that volatile does not imply that the object is suddenly thread-safe.
🌟Race Conditions
A race condition happens when the outcomes of multiple threads running the same piece of code vary depending on the order in which the threads execute. When one thread performs a 'check-then-act' procedure while another thread updates between the check and act, a race problem occurs. As an example, consider the following code:
If a second thread updated the value of x between the check and the act, y == 30 would still return true where it shouldn't.
To protect the shared data, one typical solution is to use the Lock interface. Another solution is to use the synchronized keyword to control method access. In some circumstances, the lock may be more beneficial because it allows you to explicitly specify what you want to act as the monitor rather than the implicit monitor from synchronized.
Frequently Asked Questions
What is a spring boot?
Java Spring Boot (Spring Boot) is a tool that accelerates and simplifies the development of web applications and microservices with the Spring Framework by leveraging three basic capabilities.
How do Spring boot threads work?
The TaskExecutor interface in the Spring Framework provides abstractions for asynchronous task execution. Thread pools are referred to as executors in Java SE. The TaskExecutor interface in Spring is identical to the java interface.
How does multithreading in Spring Boot work in Microservices?
Multi-threaded, single long-lived process models are classified into two types: those that dedicate a thread to each request and those that share a single thread for all requests.
What is the Purpose of multithreading in Spring Boot?
The primary rationale for including threads in a program is to boost its performance. Performance can be expressed in a variety of ways: A web server will use many threads to execute multiple data requests at the same time.
Is spring single-threaded?
SpringMVC controllers are singletons that handle several requests at the same time. Because they are utilized in a multithreaded environment, they must be threadsafe (no shared state between executions).
Conclusion
In this article, we have learned multithreading in spring boot. Also, we have discussed some important terminologies that are required while developing the applications on multithreading in spring boot.
You can also consider our Spring Boot Course to give your career an edge over others.
That's the end of the article. I hope you all like this article.