Java CompletableFuture detailed usage tutorials and practices

1. Java CompletableFuture detailed usage tutorial

Java 8 introduces a powerful asynchronous programming tool: CompletableFuture. It provides a way to handle asynchronous computations, allowing you to retrieve results as they complete, or to combine the  CompletableFuture results of one or more. This section will analyze  CompletableFuture various aspects in detail, including creation, composition, exception handling, etc., and demonstrate its use through examples.

1.1 Create CompletableFuture

The easiest way to create  CompletableFuture is to use the no-argument constructor:

CompletableFuture<Void> future = new CompletableFuture<>();

This will create an unfinished one  CompletableFuture. You can  complete do it via method:

future.complete(null);

If you want to create an already completed one  CompletableFuture, you can use  completedFuture the method:

CompletableFuture<String> future = CompletableFuture.completedFuture("Hello, world!");

Additionally, you can use  supplyAsync methods to create an asynchronous computation  CompletableFuture:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 这里是一些长时间运行的计算
    return "Hello, world!";
});

1.2 Processing the results of CompletableFuture

CompletableFuture Provides a series of methods to process the results of asynchronous calculations. These methods each return a new one  CompletableFutureso you can chain them together to form a processing pipeline.

For example, you can use  thenApply methods to transform the result:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
        .thenApply(s -> s + ", world!");

You can also use  thenAccept methods to consume the results:

CompletableFuture.supplyAsync(() -> "Hello")
        .thenAccept(s -> System.out.println(s + ", world!"));

If you don't care about the result and just want to perform some operations after the calculation is completed, you can use  thenRun the method:

CompletableFuture.supplyAsync(() -> "Hello")
        .thenRun(() -> System.out.println("Computation finished."));

1.3 Combining CompletableFuture

CompletableFuture A series of methods are provided to combine multiple asynchronous calculations.

For example, you can use  thenCompose methods to concatenate two asynchronous calculations:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
        .thenCompose(s -> CompletableFuture.supplyAsync(() -> s + ", world!"));

You can also use  thenCombine methods to concatenate two asynchronous calculations:

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> ", world!");

CompletableFuture<String> future = future1.thenCombine(future2, String::concat);

If you have more than one  CompletableFuture, you can use  allOf a method to wait for them all to complete:

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> ", world!");

CompletableFuture<Void> future = CompletableFuture.allOf(future1, future2);

1.4 Handling CompletableFuture exceptions

CompletableFuture Two methods are provided to handle exceptions: exceptionally and  handle.

exceptionally The method accepts a function that will be called when the calculation throws an exception, and its return value will be used as the new result:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    if (true) throw new RuntimeException("Exception!");
    return "Hello, world!";
}).exceptionally(ex -> "Sorry, we have an error!");

handle method is  exceptionally similar, but it handles both normal results and exceptions:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    if (true) throw new RuntimeException("Exception!");
    return "Hello, world!";
}).handle((res, ex) -> {
    if (ex != null) {
        return "Sorry, we have an error!";
    } else {
        return res;
    }
});

The above is  CompletableFuture the main usage. By combining these methods, you can create complex asynchronous processing pipelines, greatly improving the performance and responsiveness of your program.

2. Use CompletableFuture to combine MyBatis and thread pool to insert data in batches

When dealing with big data, we often need to insert a large amount of data in the database. If we use the traditional synchronous method, it may take a long time. In this part, I will show how to use Java for  CompletableFuture asynchronous processing, combined with MyBatis and thread pool to insert 300,000 pieces of data in batches, and improve the efficiency of data processing.

2.1 Create a thread pool

First, we need to create a thread pool to execute multiple insert tasks concurrently. We can use Java  Executors classes to create thread pools:

ExecutorService executor = Executors.newFixedThreadPool(10);

This will create a thread pool with 10 threads.

2.2 Create data

Then we need to create the data to be inserted. Let's say we want to insert some user data, each user has a name and an age:

class User {
    private String name;
    private int age;

    // getters and setters...
}

We can create a method to generate user data:

List<User> generateUsers(int count) {
    List<User> users = new ArrayList<>();
    for (int i = 0; i < count; i++) {
        User user = new User();
        user.setName("User" + i);
        user.setAge(i % 100);
        users.add(user);
    }
    return users;
}

2.3 Insert data

Next, we need to create a method to insert data. We will use MyBatis  SqlSession to perform the insert operation:

void insertUsers(SqlSession sqlSession, List<User> users) {
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    for (User user : users) {
        userMapper.insert(user);
    }
    sqlSession.commit();
}

Note that we committed the transaction after inserting all users. This is because transaction commit is an expensive operation in most databases and we should minimize the number of transaction commits.

2.4 Insert data using CompletableFuture

Now, we can  CompletableFuture insert data concurrently using We split the data into batches and create one  CompletableFuture for each batch to insert:

List<User> users = generateUsers(300000);
int batchSize = 1000;
List<CompletableFuture<Void>> futures = new ArrayList<>();

for (int i = 0; i < users.size(); i += batchSize) {
    List<User> batch = users.subList(i, Math.min(users.size(), i + batchSize));
    CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
            insertUsers(sqlSession, batch);
        }
    }, executor);
    futures.add(future);
}

CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

In the above code, we first generated 300000 user data and then divided the data into batches of size 1000. For each batch, we create one  CompletableFuture to insert the data into and then  CompletableFuture add this to a list. Finally, we use  CompletableFuture.allOf to wait for everything  CompletableFuture to complete.

2.5 Close the thread pool

Finally, we need to shut down the thread pool. We can use  ExecutorService.shutdown the method to shut down the thread pool:

executor.shutdown();

This will wait for all submitted tasks to complete and then close the thread pool.

The above is  CompletableFuture the method of batch inserting data using MyBatis and thread pool. In this way, we can greatly improve the efficiency of inserting data. I hope this article can help everyone understand and use it better  CompletableFuture.

Guess you like

Origin blog.csdn.net/jam_yin/article/details/132587878