The role of multithreading

Execute tasks in parallel, fast. Why can multiple tasks be executed concurrently? Because computers now have multiple cpus.

Application scenarios

One is that subtasks can be executed asynchronously. For example, when a payment is made, an MQ message is sent asynchronously to the merchant after the payment is successful. Sending MQ messages to merchants is a subtask and can be asynchronous. It is possible to start a thread to allow the subtask to execute slowly, or at least not block the main task — this way, the client can get the result of the call faster, i.e., pay faster.

One is to perform multiple tasks concurrently. For example, in the functions section above. But concurrent execution is asynchronous in nature. For example, a Web server is a thread pool, and each request is handled by a separate thread. Why would a Web server use a thread pool? In order to start a thread to process the request asynchronously, in order not to block the main thread.

How to implement asynchrony?

From the above, the most core keyword is asynchronous, how to achieve asynchronous? Start a child thread to execute a task, the nature of a child thread is asynchronous execution, because the execution of the child thread does not affect other threads, including the main thread.

As a result, the main thread can continuously ask the child thread to handle more tasks asynchronously.

Even if a child thread is abnormal, it does not affect other threads.

What technology does that use? The thread pool.

The thread pool

Thread pools are a concept, and the specific technology is Executor. To be more specific, create a thread pool and start a child thread to perform the task.

Create a thread pool

private static ExecutorService execInsert = new ThreadPoolExecutor(20,
    150, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(600),
    new ThreadPoolExecutor.CallerRunsPolicy());
Copy the code

When creating a thread pool, use this ThreadPoolExecutor. There is no need to use any other ThreadPoolExecutor. The Alibaba specification manual says to use this one and not the other.

Alibaba Specification Manual

Why is Alibaba suggesting this? There is a memory leak for one main reason. Why might memory leak? Because the number of threads is infinite by default, the request queue size is also infinite by default.

So, for the most part, you can just use ThreadPoolExecutor. No need to use the following * * * to create the connection pools listed in alibaba’s spec without the actual size parameters configured.

Class diagram

Note: In general, just use Executor. ExecutorService can be used only if the execution results of child threads are required, and lifecycle management of the thread pool is added.

ThreadPoolExecutor is a concrete implementation, and for the most part, ThreadPoolExecutor will do just fine.

Do you need to get the results of child threads?

Because in general, start a child thread to execute the task, do not care, do not need to care. So you can just use Executor, the task class that implements the Runnable interface. If you do not need to obtain the results of the child thread, implement the Runnable interface with the child thread task class.

If you need to obtain the execution result of the thread, either the execution is successful or the execution is abnormal. At this point, the execution result needs to be retrieved, and the main thread must be blocked until the child thread succeeds. If the main thread needs to obtain the results of the child thread, it implements the Callable interface with the child thread task class.

In what application scenario would you need to obtain the results of child thread execution? The code following the main thread depends on the execution result of the child thread. If the child thread succeeds, the main thread is a kind of execution logic. If it fails, the main thread is another logic. At this point, you need to get the results of the child threads.

Such as:

Public RouteResult Route (RouteParam param) {logutil.logger. info("route input parameter is {}",param); long startTime = System.currentTimeMillis(); if(! TranType.FORWARD.getIndex().equals(param.getTrantype()) && ! TranType.REVERSE.getIndex().equals(param.getTrantype())){ throw new BizException(" tranType error, actual trantype :"+ param.getTranType ()); } / / parameter check String seqId = String. The valueOf (SnowflakeIdWorker. GenerateId ()); param.setRouteGroupKey(seqId); RouteResult routeResult = null; If (stringutils.isblank (param.getdbName ())){routeResult = routeService. RouteRule (param); }else{ routeResult = new RouteResult(); routeResult.setDbName(param.getDbName()); } routeResult.setId(seqId); QrcodeRouteInfo routeInfo = initRouteInfo(param,routeResult); / / write depots data to directing a Boolean aBoolean = routeInfoService. SaveRouteInfoMongo (routeInfo); Callable<String> call = new Callable<String>() {@override public String call(); throws Exception { long startTime = System.currentTimeMillis(); . / / perform time-consuming operation routeInfoService inserRouteInfo (routeInfo); long endTime = System.currentTimeMillis(); return "ok"; }}; String seoul = null; try { Future<String> future = execInsert.submit(call); seoul = future.get(); //get Must catch exceptions, otherwise the main thread will be affected to continue execution. If you do not capture, compilation will fail. }catch (Exception e){logutil.logger. error(" get return Exception ",e); } LogUtil.LOGGER.info("future.get() result:{},aBoolean is:{}", seoul,aBoolean); If (!" if(!" ok".equals(seoul) && ! ABoolean){log.error(" write mongodb and database exception "); Throw new BizException(" write mongodb and database simultaneously exception "); } long endTime = System.currentTimeMillis(); Logutil.logger. info("route returns object :{}, execution time :{} ms",routeResult, endtime-startTime); return routeResult; }Copy the code

As you can see from the above code and comments, how to get the result of a Callable, using the get method, the get method blocks, and now the main thread starts a child thread, but the main thread is actually synchronous now, because get blocks the result, not asynchronously. Even if the main thread is not asynchronous, but the child thread is executing asynchronously, there is some optimization because the main thread will start multiple child threads to execute multiple sub-tasks at the same time.

What’s the difference between Runnable and Callable?

Callable is suitable for scenarios where you need to know the results. As a best practice, if there are no more requirements, just start a thread and let the thread perform the task, there is no need to use Callable because it is simpler.

If the main thread needs to know the result of the child thread, it uses Callable, because Callable can get the result of the child thread.

Where is the exception thrown when a child thread executes a task? How to deal with it?

If a child thread is abnormal, it throws an exception and may print an exception log, but it does not affect other child threads because each child thread is independent.

Where did it go? It’s in the child thread.

How to deal with it? No processing is required because no other child threads are affected.

However, if you use a Callable and use get to get the result (in fact, you need a Callable only to use get, otherwise there is no need to use Callable), then the main thread must catch GET because GET gets the result of the child thread. This result includes both normal and abnormal results (that is, abnormal). So the main thread must be captured, otherwise it will affect the main thread to continue to execute the following code.

Reference:

www.cnblogs.com/thisiswhy/p…

Number of thread pools configured

Formula: twice the CPU.

But in practice, it’s usually a little more than twice the CPU.

For example, the number of core threads is 10 in theory, but it might actually be 20.

Array blocking queues are typically around 1000.

The maximum number of threads is theoretically twice the NUMBER of CPUS, 20, but in practice it could be 50, or even 100.

Idle threads that exceed the number of core threads are generally idle for a maximum of a few seconds, say 5s or 10s, and then they’re recycled.

KeepAliveTime -- when the number of threads is greater than the core, This is the maximum time that excess idle threads will wait for new tasks before terminating unit for the keepAliveTime argumentCopy the code

Production configuration

private static ExecutorService execInsert = new ThreadPoolExecutor(20,
    150, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(600),
    new ThreadPoolExecutor.CallerRunsPolicy());
Copy the code

Meituan production configuration, maximum quantity 50.

Is it necessary to use multiple thread pools?

No Chinese information found.

In the company zF production practice, the job application has a thread pool for each job, and each thread pool has a maximum of 10 threads. N jobs, which adds up to dozens of threads.

In the production practice of ys, the job application is the same. There are multiple jobs, and each job uses an independent thread pool, and each thread pool has a maximum of 20. Transactions are also multiple thread pools, just sending MQ has multiple thread pools, because multiple systems are notified of successful payments. So, you can have multiple threads, but not too many, and not too many threads per pool.

In conclusion, in general, a thread pool is the best, because it is simple and fast. Why fast? Because of multiple thread pools, it is also resource-intensive to manage.

However, if a business scenario requires multiple jobs, you can use a separate thread pool for each job and use twice as many cpus for each thread pool.

Reference:

Stackoverflow.com/questions/6… // Java concurrent programming practice recommends using different thread pools for different tasks, because thread pools work best when the tasks are the same. For example, the execution time of task A and task B is different. Task A that takes A long time may occupy many threads, resulting in the execution time of task B that takes A short time. To put it bluntly, if the task is the same, use A thread pool, because only when the task is the same, the thread pool will be the most efficient, and there will not be A long task A resulting in A short task B’s time increase.

Stackoverflow.com/questions/2…

Doc.zeroc.com/ice/3.7/cli…

reference

Tech.meituan.com/2020/04/02/…