多线程的性能优化解决

要求:1000次循环在六秒左右,目前是九秒多,不到十秒。cmd执行命令的参数path是动态的,是批量执行脚本的命令,这里只是个demo。不同设备保证不会有乱码。回复里最好有现在慢的主要原因,解决思路及优化后的代码

static ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
    @GetMapping("/run")
    public void run() {
        String path = "ipconfig";
        executorService.execute(() -> {
            try {
                Process process;
                process = new ProcessBuilder("cmd.exe", "/c", path).start();
                readStream(process.getErrorStream());
                readStream(process.getInputStream());
                //System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
            } catch (IOException e) {
                System.out.println("运行错误:" + e.getMessage());
                e.printStackTrace();
            }
        });
    }

    public void readStream(InputStream is) {
        String line;
        BufferedReader input;
        try {
            input = new BufferedReader(new InputStreamReader(is, "gbk"));
            StringBuilder CmdReturn = new StringBuilder();
            line = input.readLine();

            while (line != null) {
                line = input.readLine();
                CmdReturn.append(line);
            }
            //System.out.println(CmdReturn);
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }

把完整代码贴出来吧,

你这功能是啥,不停扫描指定目录下的文件么?并做文件读取么?

C:\Users\Administrator.jdks\corretto-1.8.0_302\bin\java.exe "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2021.2\lib\idea_rt.jar=62978:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2021.2\bin" -Dfile.encoding=UTF-8 -classpath C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\charsets.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\ext\access-bridge-64.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\ext\cldrdata.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\ext\dnsns.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\ext\jaccess.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\ext\jfxrt.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\ext\localedata.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\ext\nashorn.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\ext\sunec.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\ext\sunjce_provider.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\ext\sunmscapi.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\ext\sunpkcs11.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\ext\zipfs.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\jce.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\jfr.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\jfxswt.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\jsse.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\management-agent.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\resources.jar;C:\Users\Administrator.jdks\corretto-1.8.0_302\jre\lib\rt.jar;C:\Users\Administrator\IdeaProjects\Test001\out\production\Test001 com.company.Main
type 1 count 1000 starts at 2021-08-13 23:44:42:239
submit done at 2021-08-13 23:44:42:380
jobs done at 2021-08-13 23:45:03:611
cost time: 21372 ms
type 2 count 1000 starts at 2021-08-13 23:45:03:611
submit done at 2021-08-13 23:45:03:611
jobs done at 2021-08-13 23:45:16:078
cost time: 12467 ms

Process finished with exit code 0

package com.company;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    static ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
    static ExecutorService executorService2 = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
    public static void run2() {
        String path = "C:\\Windows\\System32\\ipconfig.exe";
        executorService2.execute(() -> {
            try {
                Process process;
                process = new ProcessBuilder(path).start();
                readStream(process.getErrorStream());
                readStream(process.getInputStream());
                //System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
            } catch (IOException e) {
                System.out.println("运行错误:" + e.getMessage());
                e.printStackTrace();
            }
        });
    }
    public static void run() {
        String path = "ipconfig";
        executorService.execute(() -> {
            try {
                Process process;
                process = new ProcessBuilder("cmd.exe", "/c", path).start();
                readStream(process.getErrorStream());
                readStream(process.getInputStream());
                //System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
            } catch (IOException e) {
                System.out.println("运行错误:" + e.getMessage());
                e.printStackTrace();
            }
        });
    }
    public static void readStream(InputStream is) {
        String line;
        BufferedReader input;
        try {
            input = new BufferedReader(new InputStreamReader(is, "gbk"));
            StringBuilder CmdReturn = new StringBuilder();
            line = input.readLine();
            while (line != null) {
                line = input.readLine();
                CmdReturn.append(line);
            }
           // System.out.println(CmdReturn);
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }

    public static void main(String[] args) {
    // write your code here
        int loopCount = 1000;
        System.out.println( "type 1 count " + loopCount+  " starts at " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000; i++) {
            run();
        }
        System.out.println("submit done at " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
        executorService.shutdown();
        while (!executorService.isTerminated()) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("jobs done at " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date(end)));

        System.out.println("cost time: " + (end - start) + " ms");

        System.out.println( "type 2 count " + loopCount+  " starts at " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
        long start2 = System.currentTimeMillis();
        for (int i = 0; i < 1000; i++) {
            run2();
        }
        System.out.println("submit done at " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
        executorService2.shutdown();
        while (!executorService2.isTerminated()) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        long end2 = System.currentTimeMillis();
        System.out.println("jobs done at " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date(end2)));

        System.out.println("cost time: " + (end2 - start2) + " ms");
    }
}



这个是创建进程执行命令,获取系统信息,与电脑性能有关,cpu 核心数越多性能越高,时间几乎都是进程启动上了
你具体需要这里面的哪些信息,可以不使用进程去获取,

能不能复用Process,感觉创建Process花费比较大

可以尝试调用系统api的方式直接调用系统命令肯定惠比较慢。

你用的是newFixed,固定数量,实际运行的命令超过fixed数量就会等待线程执行完毕才会执行剩余命令,换成cached看看吧


static ExecutorService executorService3 = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
public static void run3() {
        String path = "ipconfig";
        /**组装命令集合**/
        List<String> allCommands = new ArrayList<>();
        for(int i=1;i<1000;i++){
            allCommands.add(path);
        }

        //按100条分组(也可以多点)
        //Lists ---> guava工具包
        //<dependency>
        //    <groupId>com.google.guava</groupId>
        //   <artifactId>guava</artifactId>
        //   <version>30.0-jre</version>
        //</dependency>
        List<List<String>> partition = Lists.partition(allCommands, 100);
        for(List<String> commands : partition){
            executorService3.execute(() -> {
                try {
                    //利用一次执行多条命令 cmd /c 命令1 & 命令2 & 命令3
                    Process process = new ProcessBuilder("cmd.exe", "/c", StringUtils.join(commands.toArray(new String[]{})," & "))
                    //错误信息重定向到正常输出,减少一次io
                    .redirectErrorStream(true).start();
                    readStream(process.getInputStream());
                    //System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
                } catch (IOException e) {
                    System.out.println("运行错误:" + e.getMessage());
                    e.printStackTrace();
                }
            });
        }
    }

public static void readStream(InputStream is) {
        String line;
        BufferedReader input;
        try {
            input = new BufferedReader(new InputStreamReader(is, "gbk"));
            StringBuilder CmdReturn = new StringBuilder();
            line = input.readLine();
            while (line != null) {
                line = input.readLine();
                CmdReturn.append(line);
            }
            //简单处理
            System.out.println(CmdReturn.toString().replace("Windows IP 配置","\nWindows IP 配置"));
            if(Objects.nonNull(input)){
                input.close();
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }

type 1 count 1000 starts at 2021-08-16 23:35:48:111
submit done at 2021-08-16 23:35:48:194
jobs done at 2021-08-16 23:36:26:314
cost time: 38201 ms
type 2 count 1000 starts at 2021-08-16 23:36:26:316
submit done at 2021-08-16 23:36:26:323
jobs done at 2021-08-16 23:36:47:449
cost time: 21133 ms
type 3 count 1000 starts at 2021-08-16 23:36:47:450
submit done at 2021-08-16 23:36:47:473
jobs done at 2021-08-16 23:36:57:592
cost time: 10142 ms

Process finished with exit code 0

请参考: Java多线程性能优化_大大肉包博客-CSDN博客_java多线程优化 Java多线程性能优化大家使用多线程无非是为了提高性能,但如果多线程使用不当,不但性能提升不明显,而且会使得资源消耗更大。下面列举一下可能会造成多线程性能问题的点: 死锁 过多串行化 过多锁竞争 切换上下文 内存同步 下面分别解析以上性能隐患死锁关于死锁,我们在学习操作系统的时候就知道它产生的原因和危害,这里就不从原理上去累述了,可以从... https://blog.csdn.net/m0_37542889/article/details/82798271?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162918846116780255243309%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=162918846116780255243309&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v29-1-82798271.first_rank_v2_pc_rank_v29&utm_term=Java%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%9A%84%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E8%A7%A3%E5%86%B3&spm=1018.2226.3001.4449

貌似慢在输出流的创建和读取这块,改成用文件试一下。
Process process;
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c", path);
pb.redirectOutput(new File("c:/temp/out.txt."+i));
pb.redirectError(new File("c:/temp/error.txt."+i));
process = pb.start();
// 下面的注释掉
// readStream(process.getErrorStream());
// readStream(process.getInputStream());