1. Use Thread and Runnable
Thread and Runnable are the most basicAsynchronous programming method, directly use Thread and Runnable to create and manage threads.
public class Test { public static void main(String[] args) { ("Main thread of snail:" + ().getName()); Thread thread = new Thread(() -> { try { (1000); ("Types asynchronous thread test:"+().getName()); } catch (InterruptedException e) { (); } }); (); } }
But in daily work,Thread and Runnable are not recommended directly, because it has these disadvantages:
- High resource consumption: Each time a new thread is created, it consumes system resources, and frequent creation and destruction of threads will lead to performance degradation.
- Difficult to manage: Manually managing thread life cycle, exception handling, task scheduling, etc. is very complicated.
- Lack of scalability: The number of concurrent threads cannot be easily controlled, which can easily lead to exhaustion of system resources.
- Thread reuse problem: Every time a new thread is created, the existing thread cannot be reused, which is inefficient.
2. Use Executors to provide thread pool
For the disadvantages of Thread and Runnable, we can useThread poolYes, thread pools have these main advantages:
- It helps us manage threads, avoid increasing resource loss for creating threads and destroying threads. Because a thread is actually an object, creating an object requires a class loading process, destroying an object, and going through the GC garbage collection process, all of which require resource overhead.
- Improve response speed. If the task arrives, it will definitely be much slower than if you take a thread from the thread pool and re-create a thread to execute.
- Reuse. When the thread is used up, put it back into the pool, which can achieve the effect of reuse and save resources.
Some friends said that we can directly use the Executors provided byThread poolYes, it's very fast and convenient, such as:
- -
Simpledemo codeas follows:
public class Test { public static void main(String[] args) { ("Main thread of snail:" + ().getName()); ExecutorService executor = (3); (()->{ ("Screw thread pooling method, asynchronous thread:" + ().getName()); }); } } // Running results://The main thread of the snail: main//Tian spiral thread pool method, asynchronous thread: pool-1-thread-1
3. Use custom thread pools
Use Executors to provide thread pools, such as newFixedThreadPool, although simple and fast, its blocking queue is ten boundless!
newFixedThreadPool uses LinkedBlockingQueue as the task queue by default, and LinkedBlockingQueue is an unbounded queue (the default capacity is Integer.MAX_VALUE).If the task submission speed is much greater than the thread pool processing speed, the queue will continue to accumulate tasks,It may eventually lead to memory exhaustion.
Therefore, we generallyCustom thread pool is recommended, to enable asynchronous.
public class Test { public static void main(String[] args) { // Custom thread pool ThreadPoolExecutor executor = new ThreadPoolExecutor( 2, // Number of core threads 4, // Maximum number of threads 60, // Idle thread survival time , // Time unit new ArrayBlockingQueue<>(8), // Task queue (), // Thread factory new () // Reject policy ); ("Main thread of snail:" + ().getName()); (() -> { try { (500); // Simulation time-consuming operation ("Snail custom thread pool enables asynchronous:" + ().getName()); } catch (InterruptedException e) { (); } }); } } //Output//The main thread of the snail: main//Tiannail custom thread pool enable asynchronous: pool-1-thread-1
4. Use Future and Callable
Some friends said, what if we expect asynchronous programming to return results?
Then we canUsing Future and Callable. Future and Callable were introduced in Java 5 to handle asynchronous tasks. Callable is similar to Runnable, but it can return a result and throw exceptions.Future represents the result of asynchronous calculation。
Simple use of demo:
public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { // Custom thread pool ThreadPoolExecutor executor = new ThreadPoolExecutor( 2, // Number of core threads 4, // Maximum number of threads 60, // Idle thread survival time , // Time unit new ArrayBlockingQueue<>(8), // Task queue (), // Thread factory new () // Reject policy ); ("Main thread of snail:" + ().getName()); Callable<String> task = () -> { (1000); // Simulation time-consuming operation ("Snail custom thread pool enables asynchronous:" + ().getName()); return "Hello, official account: The little boy picking up snails!"; }; Future<String> future = (task); String result = (); // Block until the task is completed ("Async results:"+result); } } //Run result://The main thread of the snail: main//Tiannail custom thread pool enable asynchronous: pool-1-thread-1//Asynchronous result: Hello, official account: Little boy picking up snails!
5. Use CompletableFuture
CompleteFuture is introduced in Java 8 and provides more powerful asynchronous programming capabilities, supporting chain calls, exception handling, combining multiple asynchronous tasks, etc.
Simple use of demo:
public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { // Custom thread pool ThreadPoolExecutor executor = new ThreadPoolExecutor( 2, // Number of core threads 4, // Maximum number of threads 60, // Idle thread survival time , // Time unit new ArrayBlockingQueue<>(8), // Task queue (), // Thread factory new () // Reject policy ); ("Main thread of snail:" + ().getName()); CompletableFuture<String> future = (() -> { try { (1000); // Simulation time-consuming operation ("Snail CompletableFuture enables asynchronous:" + ().getName()); return "Hello, official account: The little boy picking up snails!"; } catch (InterruptedException e) { (); } return "Hello, official account: The little boy picking up snails!"; },executor); (result -> ("Async results:" + result)); (); } }
6. Use ForkJoinPool
Sometimes, we want to enable asynchronous operation, split a large task into multiple small tasks (Fork), and then merge the results of these small tasks (Join). At this time, we can use ForkJoinPool~.
ForkJoinPool is a thread pool implementation introduced in Java 7, which is specifically used to handle partitioning and governance tasks.
- Its characteristics are task splitting (Fork) and result merge (Join), and work-stealing.
- ForkJoinPool is especially suitable for handling recursive tasks or parallel tasks that can be decomposed.
Simple use of demo:
public class Test { public static void main(String[] args) { ForkJoinPool pool = new ForkJoinPool(); // Create ForkJoinPool int result = (new SumTask(1, 100)); // Submit the task and get the results ("The sum of 1 to 100 is: " + result); } static class SumTask extends RecursiveTask<Integer> { private final int start; private final int end; SumTask(int start, int end) { = start; = end; } @Override protected Integer compute() { if (end - start <= 10) { // Directly calculate small tasks int sum = 0; for (int i = start; i <= end; i++) sum += i; return sum; } else { // Split the task int mid = (start + end) / 2; SumTask left = new SumTask(start, mid); SumTask right = new SumTask(mid + 1, end); (); // Execute left task asynchronously return () + (); // Wait for the left task to complete and merge the result } } } }
7. Spring's @Async asynchronous
Spring provides @Async annotationTo implement asynchronous method calls, very convenient. Using @Async allows methods to be executed in separate threads without blocking the main thread.
The steps to use Spring @Async are actually very simple:
- Enable asynchronous support: In Spring Boot project, you need to add the @EnableAsync annotation to the configuration class or main application class.
- Tag asynchronous methods: Add the @Async annotation to methods that require asynchronous execution.
- Configure thread pool: By default, Spring uses a simple thread pool. If you need to customize the thread pool, it can be implemented through configuration.
Enable asynchronous support:
@SpringBootApplication @EnableAsync // Enable asynchronous supportpublic class AsyncDemoApplication { public static void main(String[] args) { (, args); } }
Asynchronous service class
@Service public class TianLuoAsyncService { @Async // Mark as an asynchronous method public void asyncTianLuoTask() { try { (2000); // Simulation time-consuming operation ("Async task completed, thread: " + ().getName()); } catch (InterruptedException e) { (); } } }
By default, Spring uses a simple thread pool (SimpleAsyncTaskExecutor
), a new thread is created every time the call is called. Therefore, when using Spring's @Async for asynchronous use, it is recommended to use a custom thread pool.
as follows:
@Configuration public class AsyncConfig { @Bean(name = "taskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); (10); // Number of core threads (20); // Maximum number of threads (50); // Queue capacity ("AsyncThread-"); // Thread name prefix (); return executor; } }
Then, specify the name of the thread pool in the @Async annotation:
@Async("taskExecutor") // Specify the use of a custom thread poolpublic void asyncTianLuoTask() { // Method logic}
8. MQ implements asynchronous
When we mention MQ, we often mention that it hasAsynchronous processing, decoupling, flow cutting. Yes,MQ is often used to implement asynchronous programming。
The brief code is as follows:Save the user information first, and then send the MQ message that the registration is successful
// User registration method public void registerUser(String username, String email, String phoneNumber) { // Save user information (simplified version) (buildUser(username,email,phoneNumber)) // Send a message String registrationMessage = "User " + username + " has registered successfully."; // Send a message to the queue ("registrationQueue", registrationMessage); }
Consumers read messages from queues and send text messages or emails:
@Service public class NotificationService { // Listen to messages in the message queue and send text messages/emails @RabbitListener(queues = "registrationQueue") public void handleRegistrationNotification(String message) { // You can send text messages or emails here ("Sending registration notification: " + message); // Suppose this is the operation of sending text messages sendSms(message); // You can also make other notifications (such as sending emails, etc.) sendEmail(message); } }
9. Use the ThreadUtil of the Hutool tool library
What you can use is ThreadUtil similar to the Hutool tool library, which provides rich thread pool management andAsynchronous task schedulingFunction.
First introduce dependencies:
<dependency> <groupId></groupId> <artifactId>hutool-all</artifactId> <version>5.8.11</version> <!-- Please use the latest version --> </dependency>
The easiest one, you can use it directlyPerform asynchronous tasks
public class Test { public static void main(String[] args) { ("Main thread of snail"); ( () -> { ("Asynchronous Test of Snails:" + ().getName()); } ); } } //Output//The main thread of snail//Asynchronous test of field snail: pool-1-thread-1
The above is the detailed summary of 9 ways to implement asynchronous programming in Java backend. For more information about Java asynchronous programming, please pay attention to my other related articles!