Like again, form a habit 👏👏

preface

Hello, I’m L Labrami, and today’s ten-fold Programmer series is about using parallel streams in Java streaming computing to improve performance.

Java Stream operation believe we are not unfamiliar with the Stream to operate the collection that is quite smooth and simple, let you love it. However, in my years of development and code review experience, most people only know how to operate the stream() method and don’t know how to parallelStream().

Today we are going to take a look at the technique of using parallel streams.

Introduction to the

Java 8 introduces the Stream API, which makes it easy to iterate over collections as data streams.

Streams in Java are just wrappers for data sources, allowing us to perform batch operations on data in a convenient way.

It does not store data or make any changes to the underlying data source. Instead, it adds support for functional operations on data pipes.

ParallelStream () allows you to create iterators that execute in parallel, making it easy to exploit the flow of multiple processor cores for exceptions.

How easy is it? Take a look at the following line of code:

userList.parallelStream().forEach(user -> userService.saveOne(user));
Copy the code

You might think that allocating work over more kernels would be faster, but that’s not always the case.

The order flow

In general, most operations use sequential streams, as follows

List<Integer> listOfNumbers = Arrays.asList(1.2.3.4); 
listOfNumbers.stream().forEach(number -> 
    System.out.println(number + "" + Thread.currentThread().getName()) 
);
Copy the code

The parallel flow

Parallel flows allow us to execute code in parallel on different kernels and aggregate the results of each individual execution.

However, the order of execution is out of our control. It might change every time we run the program.

Pseudo code:

List<Integer> listOfNumbers = Arrays.asList(1.2.3.4); listOfNumbers.parallelStream().forEach(number -> 
    System.out.println(number + "" + Thread.currentThread().getName()) 
);
Copy the code

This is the result of two executions, not the same.

The code examples

We create a SpringBoot application that simulates an interface request and cycles through 500 single inserts.

First, simulate the sequential flow:

@PostMapping
public Long saveUser(a) {
    List<User> users = new ArrayList<>();
    for (int i = 0; i < 500; i++) {
        users.add(new User("Small" + i, "Female".3));
    }
    Long start = System.currentTimeMillis();
    // For better execution, use separate inserts
    users.stream().forEach(user -> userService.saveOne(user));
    Long end = System.currentTimeMillis();
    return end - start;
}
Copy the code

The execution time is 44321 ms

First, simulate parallel flow:

@PostMapping
public Long saveUser(a) {
    List<User> users = new ArrayList<>();
    for (int i = 0; i < 500; i++) {
        users.add(new User("Small" + i, "Female".3));
    }
    Long start = System.currentTimeMillis();
    // For better execution, use separate inserts
    users.parallelStream().forEach(user -> userService.saveOne(user));
    Long end = System.currentTimeMillis();
    return end - start;
}
Copy the code

The execution time is 7279 ms

conclusion

You can see how simple business processes are multiplied by using parallel flows to execute them. In the next article, we will continue to explain the principles and source code interpretation of Java 8 parallel streams.