Preface
Asynchronous programming is a means of allowing programs to run concurrently. It allows multiple things same time. When the program calls a long-running method, it will not block the current execution flow, and the program can continue to run. When the method execution is completed, it will notify the main thread to obtain its execution result as needed or The reason for the failure exception. Using asynchronous programming can greatly improve the throughput of our programs, better face higher concurrency scenarios and make better use of existing system resources, and at the same time will reduce the waiting time of users to a certain extent. In this article, let's take a look at what are the ways to use asynchronous programming
Java
Thread method
The easiest way to use asynchronous programming in the Java
Thread
to achieve. If you use JDK
version 8 or higher, you can use Lambda expression will be more concise. In order to better reflect the efficiency of asynchronous, the following provides examples of synchronous and asynchronous versions as a comparison:
/**
* @author mghio
* @since 2021-08-01
*/
public class SyncWithAsyncDemo {
public static void doOneThing() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("doOneThing ---->>> success");
}
public static void doOtherThing() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("doOtherThing ---->>> success");
}
public synchronized static void main(String[] args) throws InterruptedException {
StopWatch stopWatch = new StopWatch("SyncWithAsyncDemo");
stopWatch.start();
// 同步调用版本
// testSynchronize();
// 异步调用版本
testAsynchronize();
stopWatch.stop();
System.out.println(stopWatch);
}
private static void testAsynchronize() throws InterruptedException {
System.out.println("-------------------- testAsynchronize --------------------");
// 创建一个线程执行 doOneThing
Thread doOneThingThread = new Thread(SyncWithAsyncDemo::doOneThing, "doOneThing-Thread");
doOneThingThread.start();
doOtherThing();
// 等待 doOneThing 线程执行完成
doOneThingThread.join();
}
private static void testSynchronize() {
System.out.println("-------------------- testSynchronize --------------------");
doOneThing();
doOtherThing();
}
}
The synchronous execution is as follows:
Comment out the code of the synchronous calling version, and the result of asynchronous execution is as follows:
From the results of the two runs, it can be seen that the synchronous version takes 4002 ms
, and the asynchronous version takes 2064 ms
. The asynchronous execution time is reduced by nearly half. It can be seen that the use of asynchronous programming can greatly shorten the program running time.
The asynchronous thread code in the above example opens a thread doOneThing-Thread
main
method to asynchronously execute the doOneThing
task. At this time, the thread main
main thread, that is, the task doOneThing
and the task doOtherThing
run concurrently. After completing the doOtherThing
task, the synchronization waiting for the thread doOneThing
running is relatively simple overall.
But this example can only be used as an example. If you use the production environment, you will be at your own risk. There are two obvious problems in Thread
- There is no reuse to create threads. We know that frequent thread creation and destruction require a part of the overhead, and there is no limit to the number of threads in the example. If used improperly, the system threads may be exhausted, causing accidents. This problem can be solved by using thread pools.
- Asynchronous tasks cannot obtain the final execution result. This method in the example is not enough. At this time, you need to use the second
FutureTask
method described below.
FutureTask way
Starting from JDK 1.5
, the Future
interface and FutureTask
Future
interface have been introduced to represent asynchronous calculation results. This FutureTask
class implements only Future
interface also implements Runnable
interface A represents the results generated Runnable
. It can be in these three states:
- did not start when creating a
FutureTask
without executing theFutureTask.run()
method - has started in the process of
FutureTask.run()
- has completed in
FutureTask.run()
normal execution method results or callFutureTask.cancel(boolean mayInterruptIfRunning)
method and callingFutureTask.run()
after the occurrence of abnormal termination process approach
FutureTask
class implements the Future
of opening and canceling tasks of the 06106a8f1b3be9 interface, querying whether the tasks are completed, and obtaining calculation results. To get FutureTask
task, we can only get it by calling the getXXX()
series of methods. These methods will be blocked when the result has not come out. At the same time, the task can be of Callable
(with return results), or it can be of type Runnable
No results are returned). We modified example of the above two methods modified to return the task String
type using FutureTask
method are as follows:
private static void testFutureTask() throws ExecutionException, InterruptedException {
System.out.println("-------------------- testFutureTask --------------------");
// 创建一个 FutureTask(doOneThing 任务)
FutureTask<String> futureTask = new FutureTask<>(FutureTaskDemo::doOneThing);
// 使用线程池执行 doOneThing 任务
ForkJoinPool.commonPool().execute(futureTask);
// 执行 doOtherThing 任务
String doOtherThingResult = doOtherThing();
// 同步等待线程执行 doOneThing 任务结束
String doOneThingResult = futureTask.get();
// 任务执行结果输出
System.out.println("doOneThingResult ---->>> " + doOneThingResult);
System.out.println("doOtherThingResult ---->>> " + doOtherThingResult);
}
Using the FutureTask
asynchronous programming method Thread
method. The essence is to start a new thread to do the doOneThing
task and wait for the return. The results are as follows:
In this example, doOneThing
and doOtherThing
are all value-returning task (return String
type result), we are in the main thread main
create an asynchronous task FutureTask
to perform doOneThing
, then use ForkJoinPool.commonPool()
create a thread pool (about ForkJoinPool
described see Here ), and then call the execute
method of the futureTask
to the thread pool for execution.
Through the example, we can see that although FutureTask
provides some methods for us to obtain the execution result of the task, whether the task is completed, etc., it is still more complicated to use. In some more complex scenarios (such as the relationship between FutureTask
it is quite cumbersome, or as we call getXXX()
time series method still before the task is finished block the calling thread can not reach the effect of asynchronous programming, based on these issues, in JDK 8
introduced in CompletableFuture
class, let's look at how to use CompletableFuture
To achieve asynchronous programming.
CompletableFuture way
JDK 8
introduced CompletableFuture
class that implements Future
and CompletionStage
interface that provides a series asynchronous programming method, such as supplyAsync
, runAsync
and thenApplyAsync
etc., in addition CompletableFuture
Another important feature is that it allows two or more CompletableFuture
performs operations to produce results. code show as below:
/**
* @author mghio
* @since 2021-08-01
*/
public class CompletableFutureDemo {
public static CompletableFuture<String> doOneThing() {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "doOneThing";
});
}
public static CompletableFuture<String> doOtherThing(String parameter) {
return CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return parameter + " " + "doOtherThing";
});
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
StopWatch stopWatch = new StopWatch("CompletableFutureDemo");
stopWatch.start();
// 异步执行版本
testCompletableFuture();
stopWatch.stop();
System.out.println(stopWatch);
}
private static void testCompletableFuture() throws InterruptedException, ExecutionException {
// 先执行 doOneThing 任务,后执行 doOtherThing 任务
CompletableFuture<String> resultFuture = doOneThing().thenCompose(CompletableFutureDemo::doOtherThing);
// 获取任务结果
String doOneThingResult = resultFuture.get();
// 获取执行结果
System.out.println("DoOneThing and DoOtherThing execute finished. result = " + doOneThingResult);
}
}
The execution results are as follows:
In the main thread main
the first call method doOneThing()
ways to open an asynchronous task and returns the corresponding CompletableFuture
object, we named doOneThingFuture
, then doOneThingFuture
use based on CompletableFuture
the thenCompose()
way for doOneThingFuture
method after the completion of the implementation, The asynchronous task created using its execution result as doOtherThing(String parameter)
We do not need to explicitly use ExecutorService
, in CompletableFuture
used internally Fork/Join
framework for asynchronous processing tasks, therefore, it makes asynchronous code we write more concise. In addition, the CompletableFuture
class is very powerful and it provides many convenient methods. For more information about CompletableFuture
, please refer to the article .
Summarize
This article describes the Java
in JDK
are three ways to use asynchronous programming, these are the tools we have to achieve the most basic asynchronous programming, as well as above it Guava
library provides ListenableFuture and Futures class and Spring
framework Provide asynchronous execution capabilities, use @Async
and other annotations to achieve asynchronous processing, if you are interested, you can learn and understand by yourself.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。