在Java程序上实现多线程

在Java程序上实现多线程,java,multithreading,Java,Multithreading,我正在编写一个小Java程序,它使用cmd中的PsExec.exe通过ProcessBuilder启动,在联网PC上复制并安装应用程序(需要安装的PC数量可能从5到50不等) 如果我依次为每台电脑启动了ProcessBuilder,该程序运行良好 然而,为了加快速度,我想实现某种形式的多线程,它允许我同时安装5台PC(一批5个Processbuilder进程,直到所有PC都安装完毕) 我在考虑将固定线程池与可调用接口结合使用(每次执行PsExec都会返回一个值,该值指示执行是否成功,并且我必须评

我正在编写一个小Java程序,它使用cmd中的
PsExec.exe
通过
ProcessBuilder
启动,在联网PC上复制并安装应用程序(需要安装的PC数量可能从5到50不等)

如果我依次为每台电脑启动了
ProcessBuilder
,该程序运行良好

然而,为了加快速度,我想实现某种形式的多线程,它允许我同时安装5台PC(一批5个
Processbuilder
进程,直到所有PC都安装完毕)

我在考虑将固定线程池与可调用接口结合使用(每次执行
PsExec
都会返回一个值,该值指示执行是否成功,并且我必须评估该值)

用于Process Builder的代码为:

            // Start iterating over all PC in the list:
            for(String pc : pcList)
            {
                counter++;

                logger.info("Starting the installation of remote pc: " + pc); 
                updateMessage("Starting the installation of remote pc: " + pc); 


                int exitVal = 99;                    
                logger.debug("Exit Value set to 99");

                try 
                {                        
                    ProcessBuilder pB = new ProcessBuilder();
                    pB.command("cmd", "/c", 
                            "\""+psExecPath+"\"" + " \\\\" + pc + userName + userPassword + " -c" + " -f" + " -h" + " -n 60 " + 
                                    "\""+forumViewerPath+"\"" + " -q "+ forumAddress + remotePath + "-overwrite");                        

                    logger.debug(pB.command().toString()); 

                    pB.redirectError();
                    Process p = pB.start();
                    InputStream stErr = p.getErrorStream();
                    InputStreamReader esr = new InputStreamReader(stErr);
                    BufferedReader bre = new BufferedReader(esr);

                    String line = null;

                    line = bre.readLine();

                    while (line != null)
                    {
                        if(!line.equals(""))
                            logger.info(line);                
                        line = bre.readLine();
                    }
                    exitVal = p.waitFor();
                } catch (IOException ex) 
                {
                    logger.info("Exception occurred during installation of PC: \n"+pc+"\n "+ ex);
                    notInstalledPc.add(pc);
                }

                if(exitVal != 0)
                {
                    notInstalledPc.add(pc);
                    ret = exitVal;
                    updateMessage("");                    
                    updateMessage("The remote pc: " + pc + " was not installed"); 
                    logger.info("The remote pc: " + pc + " was not installed. The error message returned was: \n"+getError(exitVal) + "\nProcess exit code was: " + exitVal);
                }
                else
                {
                    updateMessage("");                    
                    updateMessage("The remote pc: " + pc + " was succesfully installed");                          
                    logger.info("The remote pc: " + pc + " was succesfully installed");                                                  
                }
现在,我已经阅读了一些关于如何实现Callable的信息,我想将我的
ProcessBuilder
放在一个可调用的界面中,然后提交for循环中运行的所有任务


我走对了吗?

你肯定能做到。我想您希望使用Callable而不是runnable来获取您的
exitVal
的结果

您的线程之间似乎没有任何共享数据,所以我认为您应该没事。因为您甚至知道要生成多少个可调用项,所以可以创建一个可调用项集合,然后

List<Future<SomeType>> results = pool.invokeAll(collection) 
List results=pool.invokeAll(集合)
这样可以更容易地处理结果。在决定是否使用线程池时,您需要弄清楚的最重要的事情可能是,如果程序在线程仍在运行时终止,该怎么办;您是否必须完成线程中的工作,是否需要无缝处理错误等

查看java线程池文档: 或者在网上搜索,有大量关于何时使用或不使用线程池的帖子/博客


但看起来你走对了路

谢谢您的回复!这肯定让我走上了正确的道路。我最终实现了如下:

ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5); //NEW                                

            List<Future<List<String>>> resultList = new ArrayList<>(); 

            updateMessage("Starting the installation of all remote pc entered..."); 

            // Start iterating over all PC in the list: 
            for(String pc : pcList)
            {
                counter++;

                logger.debug("Starting the installation of remote pc: " + pc); 

                psExe p = new psExe(pc);
                Future<List<String>> result = executor.submit(p);//NEW
                resultList.add(result);
            }

            for(Future<List<String>> future : resultList)
            {.......
ThreadPoolExecutor executor=(ThreadPoolExecutor)Executors.newFixedThreadPool(5)//新的
List resultList=new ArrayList();
updateMessage(“开始安装所有输入的远程pc…”);
//开始迭代列表中的所有PC:
用于(字符串pc:pcList)
{
计数器++;
logger.debug(“开始安装远程pc:+pc”);
psExe p=新的psExe(pc);
未来结果=executor.submit(p);//新建
结果列表。添加(结果);
}
for(未来:结果列表)
{.......
在最后一个for循环中,我读取操作的结果,并将其写入屏幕或根据返回的结果进行操作

我还有几个问题,因为我还不太清楚:

1-如果我有20个PC,并在我的第一个For循环中将所有可调用线程提交到池中,那么我是否正确地得到只启动5个线程的消息(threadpool size=5)但是,所有线程都将被创建并处于等待状态,只有在第一个运行的线程完成并返回结果值后,下一个线程才会自动启动,直到所有PC机完成

2-与我使用的方法(for循环中的submit()相比,您建议使用invokeall()有什么区别(优势)


再次感谢您的帮助…我真的很喜欢这个Java东西!!;-)

太好了,这正是我想要的,干得好!至于您的问题:1:当您创建一个大小为5的fixedthreadpool时,无论您是否有5个任务分配到池中,都会启动5个线程。如果有5个以上的任务,它们只需按照您的说法等待即可。2:invokeAll是sim这是因为你似乎有这么少的任务,但是如果你有更多的任务,你可能应该考虑使用提交或一个时间可变的变量,以即时处理结果。如果要在每个线程完成后立即获得一个结果,而不必等待池中的所有线程完成,那么实现这一点的最佳方法是什么?我可以告诉每个线程在完成后立即通知我结果吗?谢谢