实践-创建多少线程合适

创建多少线程合适?

根据文章Java线程(中):创建多少线程才是合适的?

我总结了

  • 为什么要使用多线程?

    因为程序执行多任务时,可以执行的更快,快 取决于 延迟吞吐量 。降低延迟,提高吞吐量

  • 多线程的应用场景有哪些?
    io密集型:磁盘和内存打交道之类的操作
    cpu密集型:复杂计算,几乎不操作磁盘,就是一个劲的计算

  • 线程数如何创建?

    cpu密集型 线程数和cpu核数相等就行(不过在工程上,线程的数量一般会设置为“CPU 核数 +1”),为什么呢?

    1. 假设只有cpu计算,io操作很少甚至没有的一个场景;

      • 线程数如果少于cpu核数,如果计算量充足的情况下,每个线程都是正常工作,没有线程切换,但是存在cpu核没有利用到的情况,这是cpu利用率不足。
      • 线程数如果多于CPU核数,如果计算量充足的情况下,那么多出来的线程会争抢CPU资源,造成线程切换的成本。

​ io密集型 最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)] (工程上,一般是cpu核数*2+1
20220706173915

针对io密集型和cpu密集型的合适线程数实践

本机配置

20220706174229

实验代码

/**
 * @author ggBall
 * @version 1.0.0
 * @ClassName IoTest.java
 * @Description TODO
 * @createTime 2022年07月06日 14:48:00
 */
public class PerformanceTest {
    
    

    String classFilePath = PerformanceTest.class.getResource("").getPath();

    CountDownLatch latch;

    /**
     * 按照公式 io密集型 线程数=核数*2+1 本机8核 线程数应该是17
     * 结果 20次的10000次io操作 (8核*2+1)10个线程操作 平均1705.3ms
     * 结果 20次的10000次io操作 (8核*2+1)11个线程操作 平均1605.35ms
     * 结果 20次的10000次io操作 (8核*2+1)12个线程操作 平均1595.45ms
     * 结果 20次的10000次io操作 (8核*2+1)13个线程操作 平均1625.1ms
     * 结果 20次的10000次io操作 (8核*2+1)14个线程操作 平均1648.65ms
     * 结果 20次的10000次io操作 (8核*2+1)15个线程操作 平均1654.95ms
     * 结果 20次的10000次io操作 (8核*2+1)16个线程操作 平均1655.3ms
     * 结果 20次的10000次io操作 (8核*2+1)17个线程操作 平均1645.5ms
     * 结果 20次的10000次io操作 (8核*2+1)18个线程操作 平均1645.6ms
     * 结果 20次的10000次io操作 (8核*2+1)19个线程操作 平均1661.35ms
     * 结果 20次的10000次io操作 (8核*2+1)20个线程操作 平均1675.95ms
     * @throws InterruptedException
     */
    @Test
    public void ioTest() throws InterruptedException {
    
    

        int ioTimes = 10000;
        int threadNum;
        for (int i1 = 10; i1 <= 20; i1++) {
    
    
            threadNum = i1;
            ArrayList<Long> times = new ArrayList<>();
            for (int i = 0; i < 20; i++) {
    
    
                long time = singleIo(ioTimes, threadNum);
                times.add(time);
            }
            Double avgTime = times.stream().collect(Collectors.averagingLong(item -> item));
//            System.out.println(times);
            System.out.println("结果 20次的"+ioTimes+"次io操作 (8核*2+1)"+threadNum+"个线程操作 平均"+avgTime+"ms");
        }



    }

    /**
     * 按照公式 cpu密集型 线程数=核数 本机8核 线程数应该是8
     * 结果 20次的10000次cpu操作 (8核*2+1)15个线程操作 平均2.0ms
     * 结果 20次的10000次cpu操作 (8核*2+1)14个线程操作 平均1.85ms
     * 结果 20次的10000次cpu操作 (8核*2+1)13个线程操作 平均2.0ms
     * 结果 20次的10000次cpu操作 (8核*2+1)12个线程操作 平均1.6ms
     * 结果 20次的10000次cpu操作 (8核*2+1)11个线程操作 平均1.5ms
     * 结果 20次的10000次cpu操作 (8核*2+1)10个线程操作 平均1.6ms
     * 结果 20次的10000次cpu操作 (8核*2+1)9个线程操作 平均1.55ms
     * 结果 20次的10000次cpu操作 (8核*2+1)8个线程操作 平均1.35ms
     * 结果 20次的10000次cpu操作 (8核*2+1)7个线程操作 平均1.4ms
     * 结果 20次的10000次cpu操作 (8核*2+1)6个线程操作 平均1.6ms
     * 结果 20次的10000次cpu操作 (8核*2+1)5个线程操作 平均1.45ms
     * 结果 20次的10000次cpu操作 (8核*2+1)4个线程操作 平均1.4ms
     * 结果 20次的10000次cpu操作 (8核*2+1)3个线程操作 平均1.0ms
     * 结果 20次的10000次cpu操作 (8核*2+1)2个线程操作 平均1.7ms
     * @throws InterruptedException
     */
    @Test
    public void cpuTest() throws InterruptedException {
    
    
        int cpuTimes = 10000;
        int threadNum;
        for (int i1 = 16; i1 > 1; i1--) {
    
    
            threadNum = i1;
            ArrayList<Long> times = new ArrayList<>();
            for (int i = 0; i < 20; i++) {
    
    
                long time = singleCPU(cpuTimes, threadNum);
                times.add(time);
            }
            Double avgTime = times.stream().collect(Collectors.averagingLong(item -> item));
//            System.out.println(times);
            System.out.println("结果 20次的"+cpuTimes+"次cpu操作 (8核*2+1)"+threadNum+"个线程操作 平均"+avgTime+"ms");
        }

    }

    /**
     * 执行单次多线程cpu任务
     * @param cpuTimes
     * @param threadNum
     * @return
     * @throws InterruptedException
     */
    private long singleCPU(int cpuTimes, int threadNum) throws InterruptedException {
    
    

        latch = new CountDownLatch(cpuTimes);

        long start = System.currentTimeMillis();
        // 170次io操作 (8核*2+1)17个线程操作
        ExecutorService executorService = Executors.newFixedThreadPool(threadNum);

        for (int i = 0; i < cpuTimes; i++) {
    
    
            CPUTasker tasker = new CPUTasker();
            executorService.submit(tasker);
        }

        latch.await();

        long end = System.currentTimeMillis();

//        System.out.println(end-start+"ms");
        return end-start;
    }


    /**
     * 执行单次多线程io任务
     * @param ioTimes
     * @param threadNum
     * @return
     * @throws InterruptedException
     */
    public long singleIo(int ioTimes,int threadNum) throws InterruptedException {
    
    
        latch = new CountDownLatch(ioTimes);

        long start = System.currentTimeMillis();
        // 170次io操作 (8核*2+1)17个线程操作
        ExecutorService executorService = Executors.newFixedThreadPool(threadNum);

        for (int i = 0; i < ioTimes; i++) {
    
    
            IoTasker tasker = new IoTasker();
            executorService.submit(tasker);
        }

        latch.await();

        long end = System.currentTimeMillis();

//        System.out.println(end-start+"ms");
        return end-start;
    }

    /**
     * io任务
     */
    class IoTasker implements Runnable{
    
    

        @Override
        public void run() {
    
    
            String fileName = "text.txt";
            FIleUtils fIleUtils = new FIleUtils();
            fIleUtils.writeFile(classFilePath+ "/" + fileName,"sda实打实大sss11212");
            try {
    
    
                fIleUtils.fileRead(classFilePath+ "/" + fileName);
//                System.out.println(Thread.currentThread().getName()+"io完成");
                latch.countDown();

            } catch (NoSuchFieldException e) {
    
    
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * cpu任务
     */
    class CPUTasker implements Runnable{
    
    

        @Override
        public void run() {
    
    
            for (int i = 0; i < 1024; i++) {
    
    
                for (int j = 0; j < 1024; j++) {
    
    
                    for (int k = 0; k < 1024; k++) {
    
    
                        
                    }
                }
            }
            latch.countDown();
        }
    }


}


实践总结

从实验代码看,io密集型设置的线程数 确实符合 线程数=核数*2+1 公式,测试结果显示17个线程耗时最少;但是cpu密集型 从线程数3~12个 耗时都在1.5ms左右,与公式线程数=核数相差很大,
所以设置线程的公式只是参考标准,生产环境还得实际测试

问题

实践总结

从实验代码看,io密集型设置的线程数 确实符合 线程数=核数*2+1 公式,测试结果显示17个线程耗时最少;但是cpu密集型 从线程数3~12个 耗时都在1.5ms左右,与公式线程数=核数相差很大,
所以设置线程的公式只是参考标准,生产环境还得实际测试

问题

I/O 耗时 和 CPU 耗时 不知道使用什么工具测试?

猜你喜欢

转载自blog.csdn.net/ZHUXIUQINGIT/article/details/125645568