[elegant code] line 04-1 completes multithreading. Stop writing runnable

Posted by exxx on Thu, 06 Jan 2022 01:27:33 +0100

[elegant code] line 04-1 completes multithreading. Stop writing runnable

Welcome to b station official account / public number [hexagon warrior Xia Ning], a man who wants to fill all the indicators. The article has been published in github directory Included.
The handsome and beautiful in front of the screen, if it helps you, please like it and add a collection, which is really important to me. Don't worry about where you go next time.

1. Background introduction

The completable future and anonymous functions provided by Java 8 allow us to multithread one line of code

2. Establish related categories

2.1.ThreadEntity

Entity classes for multithreaded testing

public class ThreadEntity {
    private int num;
    private int price;
    public int countPrice(){
        price = RandomUtils.nextInt();
        try {
            System.out.println(num);
            // Wait randomly for 1 ~ 10 seconds
            Thread.sleep(RandomUtils.nextInt(1, 10) * 1000);
            System.out.println(num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return price;
    }
}

2.2.ThreadPoolManager

/**
 * tasks The number of tasks per second, 200 by default, is calculated based on the number of accesses and where the thread pool is used
 * taskCost: The time spent on each task is 0.1s by default
 * responseTime: The maximum response time is 1s by default, and the maximum endurance time of general users is 3 seconds
 *
 * @author seal email:876651109@qq.com
 * @date 2020/5/30 10:08 AM
 */
@Data
@Slf4j
@Configuration
public class ThreadPoolManager {
    /**
     * The average response time defaults to 2 seconds
     */
    private static final float ALL_COST_AVG = 2F;
    /**
     * The average IO time defaults to 1.5 seconds
     */
    private static final float IO_COST_AVG = 1.5F;
    /**
     * Server cores
     */
    private static final int SIZE_PROCESSOR = Runtime.getRuntime().availableProcessors();
    /**
     * https://www.cnblogs.com/dennyzhangdd/p/6909771.html?utm_source=itdadao&utm_medium=referral
     * Blocking coefficient = blocking time / (blocking time + calculation time)
     * Number of threads = number of cores / (1-blocking coefficient)
     * Equivalent to the number of cpu cores * cpu utilization * (1 + ratio of waiting time to calculation time)
     * N+1 Usually optimal efficiency
     * <p>
     * https://blog.51cto.com/13527416/2056080
     */
    private static final int SIZE_CORE_POOL = SIZE_PROCESSOR + 1;

    /**
     * The maximum number of thread pool maintenance will be consistent with the number of core threads by default, which is meaningless. In a conservative case, 2cpu is taken
     * Or simply calculate the number of CPUs with thread pool size = ((thread IO time + thread CPU time) / thread CPU time)**
     * Time consumed by request / (time consumed by request - DB processing) * number of CPUs, focusing on cpu waiting time, usually database db time
     * According to the usual 2-second display interface, the database operation takes 1.5 seconds (2/0.5)*n, which is actually optimizing the waiting time
     * <p>
     * The default 4n is 8-core 32 threads
     */
    private static final int SIZE_MAX_POOL = (int) (SIZE_PROCESSOR * (ALL_COST_AVG / (ALL_COST_AVG - IO_COST_AVG)));
    /**
     * The queue length of the thread pool is integer maximum by default. Dubbo uses 1000. Unlimited will cause the user's tasks to queue all the time. You should select the appropriateness to discard,
     * Tolerable time 6, others are abandoned
     * SIZE_MAX_POOL/IO_COST_AVG=The number of tasks that can be processed per second. The default is
     * Tolerable time 6 * number of tasks per second = X number of queues
     */
    private static final int SIZE_QUEUE = (int) (6 * (SIZE_MAX_POOL / IO_COST_AVG));
    /**
     * Thread pool concrete class
     * LinkedBlockingDeque It is commonly used for fixed threads, and SynchronousQueue is commonly used for cache thread pool
     * Executors.newCachedThreadPool()Often used for short-term tasks
     * <p>
     * Thread factory selection, little difference
     * spring customizablethreadfactory and new customizablethreadfactory ("springthread pool -")
     * guava Threadfactorybuilder, new threadfactorybuilder() setNameFormat("retryClient-pool-"). build();
     * apache-lang Basicthreadfactory, new basicthreadfactory Builder(). namingPattern("basicThreadFactory-"). build()
     * <p>
     * Default AbortPolicy for policies with full queues
     */
    private static ThreadPoolManager threadPoolManager = new ThreadPoolManager();

    private final ThreadPoolExecutor pool = new ThreadPoolExecutor(
            SIZE_CORE_POOL,
            SIZE_MAX_POOL,
            30L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(SIZE_QUEUE),
            new CustomizableThreadFactory("springThread-pool-"),
            new ThreadPoolExecutor.AbortPolicy()
    );

    private void prepare() {
        if (pool.isShutdown() && !pool.prestartCoreThread()) {
            int coreSize = pool.prestartAllCoreThreads();
            System.out.println("Current thread pool");
        }
    }


    public static ThreadPoolManager getInstance() {
        if (threadPoolManager != null) {
            ThreadPoolExecutor pool = threadPoolManager.pool;
        }
        return threadPoolManager;
    }
}

3. Core code

3.1. Parallel stream

Parallel is a parallel core. It can be found that there are multiple threads running inside, but the order will be arranged after collect ion, so don't worry. Small projects can be used. For large projects, it is recommended to be honest and use your own thread pool. The fork/join provided by JDK does not fit the business

System.out.println(Stream.of(1, 2, 3, 4, 5, 6).parallel().map(l -> {
    System.out.println(l);
    return l;
}).collect(Collectors.toList()));

The output is as follows. The output is random because of multithreading, but the final result does not change because collect ion is used

2
6
4
5
3
1
[1, 2, 3, 4, 5, 6]

3.2. Synchronization code

This can eliminate the need to implement the thread interface, but we still need to consider the discarding when the queue is full

List<ThreadEntity> listEntity = IntStream.range(0, 10).mapToObj(x -> ThreadEntity.builder().num(x).build()).collect(Collectors.toList());
List<CompletableFuture<Integer>> listCompletableFuture = listEntity.stream().map(x -> {
    try {
        // Here threadpoolmanager getInstance(). If getpool () does not pass this parameter, the default commonPool will be used. trycatch is generally not written if there are no special requirements
        return CompletableFuture.supplyAsync(() -> x.countPrice(),
                ThreadPoolManager.getInstance().getPool());
    } catch (RejectedExecutionException e) {
        System.out.println("reject" + x);
        log.error("", e);
        return null;
    }
}).collect(Collectors.toList());
List<Integer> result = listCompletableFuture.stream().map(CompletableFuture::join).collect(Collectors.toList());
System.out.println(result);
System.out.println(listEntity);

The output is as follows. You can see that the operation is carried out in a multi-threaded manner, but the results are consistent with the original

start6
start9
start0
start3
start2
start1
start8
start5
start4
start7
end3
end8
end5
end7
end9
end1
end2
end6
end0
end4
[131523688, 1491605535, 222657954, 132274662, 1134597171, 2057763841, 1168687436, 1842194861, 1264173480, 56446450]
[ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@7d6f201, num=0, price=131523688), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@58e825f3, num=1, price=1491605535), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@d458bb1, num=2, price=222657954), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@7e26830, num=3, price=132274662), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@43a0a2b8, num=4, price=1134597171), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@7aa70ac1, num=5, price=2057763841), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@45a8d047, num=6, price=1168687436), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@6dcdb8e3, num=7, price=1842194861), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@4b59d119, num=8, price=1264173480), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@35d5d9e, num=9, price=56446450)]

If intstream If range (0, 10) is changed to (0, 1000), the following error will be rejected

java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.CompletableFuture$AsyncSupply@5af97850 rejected from java.util.concurrent.ThreadPoolExecutor@491666ad[Running, pool size = 64, active threads = 64, queued tasks = 256, completed tasks = 0]
    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
    at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
    at java.util.concurrent.CompletableFuture.asyncSupplyStage(CompletableFuture.java:1618)
    at java.util.concurrent.CompletableFuture.supplyAsync(CompletableFuture.java:1843)
    at com.example.demo.lesson.grace.thread.TestMain.lambda$threadEx1$2(TestMain.java:34)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1384)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at com.example.demo.lesson.grace.thread.TestMain.threadEx1(TestMain.java:41)
    at com.example.demo.lesson.grace.thread.TestMain.main(TestMain.java:26)
rejectThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@1a9, num=366)

3.3. Asynchronous code

The following code can be abbreviated directly into one line, which makes it extremely convenient to deal with asynchronous tasks
CompletableFuture.runAsync(() -> fun())

List<ThreadEntity> listEntity = IntStream.range(0, 500).mapToObj(x -> ThreadEntity.builder().num(x).build()).collect(Collectors.toList());
List<CompletableFuture> listCompletableFuture = listEntity.stream().map(x -> {
    try {
        // Here threadpoolmanager getInstance(). If getpool () does not pass this parameter, the default commonPool will be used. trycatch is generally not written if there are no special requirements
        return CompletableFuture.runAsync(() -> x.countPrice(), ThreadPoolManager.getInstance().getPool());
    } catch (RejectedExecutionException e) {
        System.out.println("reject" + x);
        return null;
    }
}).collect(Collectors.toList());
listCompletableFuture.stream().map(CompletableFuture::join);
System.out.println("1234");
// One line multithreading asynchronous execution writing method
CompletableFuture.runAsync(() -> System.out.println(1));

The output is as follows. You can see that the main thread has ended and other sub threads are outputting. There are no waiting multithreads at all

1234
1
start7
start0
start6
start5
start4
start2
start8
start1
start9
start3
end8
end4
end9
end6
end2
end0
end1
end3
end5
end7