Java multi-thread optimization methods and usage methods, java multi-thread Optimization Methods

Source: Internet
Author: User

Java multi-thread optimization methods and usage methods, java multi-thread Optimization Methods

I. Introduction to multithreading

In programming, we will inevitably encounter multi-threaded programming problems, because concurrent processing is required in most business systems. In concurrency scenarios, multithreading is very important. In addition, during the interview, the interviewer usually asks us about multithreading, for example, how to create a thread? We usually answer this question in two ways: Inherit the Thread class and override the run method; implement the Runnable interface and override the run method. Then the interviewer will certainly ask where the advantages and disadvantages of the two methods are. In any case, we will come to a conclusion, that is, method 2, because object-oriented systems advocate less inheritance, use combinations as much as possible.

At this time, we may also think about what to do if we want to get the return value from multiple threads? Based on our learned knowledge, we will think of implementing the Callable interface and rewriting the call method. So how can I use multithreading in a project? How many methods does it have?

First, let's look at an example:

 

This is a simple method to create multiple threads, which is easy to understand. In this example, we can input different parameters in Thread () to implement different business logic based on different business scenarios, however, the problem that this method creates multiple threads is that it creates threads repeatedly and has to be destroyed after the threads are created. If the concurrent scenario has low requirements, this method may seem acceptable, however, in high concurrency scenarios, this method does not work, because the creation of thread destruction is very resource-consuming. Therefore, based on experience, the correct method is to use the thread pool technology. JDK provides a variety of thread pool types for us to choose from. For details, refer to the jdk documentation.

 

Here, we need to note that the input parameters represent the number of threads we have configured. Is it better to have more threads? Certainly not. Because we need to fully consider the server performance when configuring the number of threads. If there are many threads configured, the server performance may not be optimal. Generally, the number of threads is determined by the number of computers. When the number of threads reaches the peak value, it cannot be calculated. If it is CPU-consuming business logic (more computing), the number of threads and the number of cores will reach the peak, if it is I/O-consuming business logic (operating the database, file Upload, download, etc.). The more threads, the better the performance.

Another formula for setting the number of threads is as follows:

Y = N * (a + B)/a), where N: Number of CPU cores, a: computing time of the program during thread execution, B: When the thread executes, the blocking time of the program. With this formula, the number of threads in the thread pool will be constrained. We can flexibly configure the number based on the actual situation of the machine.

Ii. multi-thread optimization and Performance Comparison

The thread technology has been used in recent projects and has encountered a lot of troubles during use. Take advantage of the heat and sort out the performance comparison of several multi-threaded frameworks. Currently, there are roughly three types of knowledge: ThreadPool (thread pool) + CountDownLatch (program counter), Fork/Join framework, and JDK8 parallel stream, the following is a summary of the performance of these multi-threaded processing methods.

First, assume that a business scenario generates multiple file objects in the memory. Here, we tentatively set the 30000 (Thread. sleep (time) thread sleep simulates business processing business logic to compare the processing performance of multiple threads in these methods.

1) single thread

This method is very simple, but it takes a long time to process the program, because each thread will be executed only after the current thread is executed, it has little to do with multithreading, so the efficiency is very low.

First, create a file object. The Code is as follows:

Public class FileInfo {private String fileName; // file name private String fileType; // file type private String fileSize; // file size private String fileMD5; // MD5 code private String fileVersionNO; // file version number public FileInfo () {super ();} public FileInfo (String fileName, String fileType, String fileSize, String fileMD5, String fileVersionNO) {super (); this. fileName = fileName; this. fileType = fileType; this. fileSize = fileSize; this. fileMD5 = fileMD5; this. fileVersionNO = fileVersionNO;} public String getFileName () {return fileName;} public void setFileName (String fileName) {this. fileName = fileName;} public String getFileType () {return fileType;} public void setFileType (String fileType) {this. fileType = fileType;} public String getFileSize () {return fileSize;} public void setFileSize (String fileSize) {this. fileSize = fileSize;} public String getFileMD5 () {return fileMD5;} public void setFileMD5 (String fileMD5) {this. fileMD5 = fileMD5;} public String getFileVersionNO () {return fileVersionNO;} public void setFileVersionNO (String fileVersionNO) {this. fileVersionNO = fileVersionNO ;}

Next, simulate business processing, create 30000 file objects, thread sleep for 1 ms, the previously set 1000 ms, found a long time, the entire Eclipse is stuck, so the time is changed to 1 ms.

Public class Test {private static List <FileInfo> fileList = new ArrayList <FileInfo> (); public static void main (String [] args) throws InterruptedException {createFileInfo (); long startTime = System. currentTimeMillis (); for (FileInfo fi: fileList) {Thread. sleep (1);} long endTime = System. currentTimeMillis (); System. out. println ("Time consumed by a single thread:" + (endTime-startTime) + "ms");} private static void createFileInfo () {for (int I = 0; I <30000; I ++) {fileList. add (new FileInfo ("front photo of ID card", "jpg", "101522", "md5" + I, "1 "));}}}

The test results are as follows:

 

It can be seen that it takes a long time to generate 30000 file objects, which is close to 1 minute, and the efficiency is relatively low.

2) ThreadPool (thread pool) + CountDownLatch (program counter)

As the name suggests, CountDownLatch is a thread counter. Its execution process is as follows: first, the await () method is called in the main thread, and the main thread is blocked. Then, the program counter is passed as a parameter to the thread object, finally, after each thread executes the task, the countDown () method is called to complete the task. After countDown () is executed multiple times, the await () of the main thread will become invalid. The implementation process is as follows:

Public class Test2 {private static ExecutorService executor = Executors. newFixedThreadPool (100); private static CountDownLatch countDownLatch = new CountDownLatch (100); private static List <FileInfo> fileList = new ArrayList <FileInfo> (); private static List <FileInfo> list = new ArrayList <> (); public static void main (String [] args) throws InterruptedException {createFileInfo (); addList (); long startTime = System. currentTimeMillis (); int I = 0; for (List <FileInfo> fi: list) {executor. submit (new FileRunnable (countDownLatch, fi, I); I ++;} countDownLatch. await (); long endTime = System. currentTimeMillis (); executor. shutdown (); System. out. println (I + "Time consumed by threads:" + (endTime-startTime) + "ms");} private static void createFileInfo () {for (int I = 0; I <30000; I ++) {fileList. add (new FileInfo ("front photo of ID card", "jpg", "101522", "md5" + I, "1") ;}} private static void addList () {for (int I = 0; I <100; I ++) {list. add (fileList );}}}

FileRunnable class:

/*** Multi-thread processing ** @ author wangsj ** @ param <T> */public class FileRunnable <T> implements Runnable {private CountDownLatch countDownLatch; private List <T> list; private int I; public FileRunnable (CountDownLatch countDownLatch, List <T> list, int I) {super (); this. countDownLatch = countDownLatch; this. list = list; this. I = I ;}@ Override public void run () {for (T t: list) {try {Thread. sleep (1);} catch (InterruptedException e) {e. printStackTrace ();} countDownLatch. countDown ();}}}

The test results are as follows:

 

3) Fork/Join framework

In Jdk 7, the Fork/join framework emerged. Literally, fork is split, and join is merge. Therefore, the idea of this framework is. The fork sharding task is used, and then the join operation is used to merge and summarize the results after each person completes the split. For example, we want to calculate the number of consecutive additions, 2 + 4 + 5 + 7 = ?, How can we use the Fork/join framework to accomplish this? The idea is to split the molecular task. We can split this operation into two subtasks, one for 2 + 4 and the other for 5 + 7, this is the Fork process. After the computation is completed, the results of the two subtasks are summarized and the sum is obtained. This is the join process.

The execution idea of the Fork/Join framework: first, divide a task and use the fork class to split a large task into several subtasks. This separation process needs to be determined according to the actual situation, until the split task is small enough. Then, the join class executes the task. The split sub-tasks are in different queues. Several threads obtain and execute the tasks from the queue respectively, and the execution results are placed in a separate queue, finally, start the thread, get the results in the queue, and merge the results.

Several Classes are required to use the Fork/Join framework. For how to use classes, refer to the jdk api. to use this framework, you must first inherit the ForkJoinTask class. Generally, you only need to inherit its subclass RecursiveTask or RecursiveAction. RecursiveTask is used for scenarios with returned results, and RecursiveAction is used for scenarios without returned results. ForkJoinTask needs to be executed using ForkJoinPool. This class is used to maintain that the split subtasks are added to different task queues.

The following is the implementation code:

Public class Test3 {private static List <FileInfo> fileList = new ArrayList <FileInfo> (); // private static ForkJoinPool forkJoinPool = new ForkJoinPool (100 ); // private static Job <FileInfo> job = new Job <> (fileList. size ()/100, fileList); public static void main (String [] args) {createFileInfo (); long startTime = System. currentTimeMillis (); ForkJoinPool forkJoinPool = new ForkJoinPool (100); // split the Job <FileInfo> Job = new Job <> (fileList. size ()/100, fileList); // The result ForkJoinTask <Integer> fjtResult = forkJoinPool returned when the task is submitted. submit (job); // block while (! Job. isDone () {System. out. println ("task completed! ");} Long endTime = System. currentTimeMillis (); System. out. println ("fork/join framework time:" + (endTime-startTime) + "ms");} private static void createFileInfo () {for (int I = 0; I <30000; I ++) {fileList. add (new FileInfo ("front photo of ID card", "jpg", "101522", "md5" + I, "1 "));}}} /*** execution task class ** @ author wangsj **/public class Job <T> extends RecursiveTask <Integer> {private static final long serialVersionUID = 1L; private int count; private List <T> jobList; public Job (int count, List <T> jobList) {super (); this. count = count; this. jobList = jobList;}/*** execute the task, which is similar to the run Method */@ Override protected Integer compute () {// split the task if (jobList. size () <= count) {executeJob (); return jobList. size ();} else {// continue to create the task until the List <RecursiveTask <Long> fork = new shard List <RecursiveTask <Long> () can be executed (); // split the molecular task. Here we use the binary int countJob = jobList. size ()/2; List <T> leftList = jobList. subList (0, countJob); List <T> rightList = jobList. subList (countJob, jobList. size (); // assign Job leftJob = new Job <> (count, leftList); Job rightJob = new Job <> (count, rightList ); // execute the task leftJob. fork (); rightJob. fork (); return Integer. parseInt (leftJob. join (). toString () + Integer. parseInt (rightJob. join (). toString () ;}}/*** execution Method */private void executeJob () {for (T job: jobList) {try {Thread. sleep (1);} catch (InterruptedException e) {e. printStackTrace ();}}}

The test results are as follows:

 

4) JDK8 parallel stream

Parallel stream is one of the new features of JDK 8. The idea is to transform the sequential execution into a concurrent stream, which is implemented by calling the parallel () method. A parallel stream divides a stream into multiple data blocks, uses different threads to process streams of different data blocks, and finally merges the processing results of each data stream, similar to the Fork/Join framework.

By default, the parallel stream uses the public thread pool ForkJoinPool. The number of threads is the default value. We can adjust the number of threads according to the number of cores of the machine. Adjust the number of threads in the following ways.

System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "100");

The following is the implementation process of the Code, which is very simple:

Public class Test4 {private static List <FileInfo> fileList = new ArrayList <FileInfo> (); public static void main (String [] args) {// System. setProperty ("java. util. concurrent. forkJoinPool. common. parallelism "," 100 "); createFileInfo (); long startTime = System. currentTimeMillis (); fileList. parallelStream (). forEach (e-> {try {Thread. sleep (1);} catch (InterruptedException f) {f. printStackTrace () ;}}); long endTime = System. currentTimeMillis (); System. out. println ("jdk8 parallel stream time consumption:" + (endTime-startTime) + "ms");} private static void createFileInfo () {for (int I = 0; I <30000; I ++) {fileList. add (new FileInfo ("front photo of ID card", "jpg", "101522", "md5" + I, "1 "));}}}

The following is a test. The number of thread pools is not set for the first time. The default value is used. The test result is as follows:

 

We can see that the result is not very satisfactory and takes a long time. Next, set the number of thread pools, that is, add the following code:

System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "100");

The test result is as follows:

 

This time is relatively time-consuming and ideal.

Iii. Summary

Taking single thread as a reference, the longest time consumption is the native Fork/Join framework. Although the number of thread pools is configured here, however, the effect is relatively accurate. The number of JDK8 parallel streams configured with the thread pool is poor. Parallel stream implementation code is simple and easy to understand. We don't need to write redundant for loops. A parallelStream method can do all the work, and the amount of code is greatly reduced. In fact, the bottom layer of the parallel stream still uses the Fork/Join framework, which requires us to use various technologies flexibly during the development process, distinguish the advantages and disadvantages of various technologies, so as to better serve us.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.