2016-01-27 97 views
0

我正在写一个小程序,它使用PsExec.exe从使用ProcessBuilder启动的cmd启动,在联网的PC上复制和安装应用程序(需要安装的PC数量可以从5到50)。在Java程序中实现多线程

如果我为每台PC依次启动ProcessBuilder,程序会正常工作。

然而,为了加快速度,我想实现某种形式的多线程这可能让我安装5 PC当时的同时(直到所有的PC已经安装了5×Processbuilder过程中的一个“批量”)。

我想组合使用一个固定的线程池与可呼叫接口的(的PsExec每个执行返回一个值,该值指示如果执行为succesfull并且我不得不评估)。

用于ProcessBuilder的代码是:

  // 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");             
       } 

现在我读过关于如何实现可调用的一些信息,我想附上我的ProcessBuilder在Callable接口,然后提交所有在for循环中运行的任务。

我在正确的轨道上吗?

回答

0

你当然可以做到这一点。我想你想用Callable而不是runnable来得到你的结果exitVal

它似乎没有任何你的线程之间共享数据,所以我认为你应该没问题。既然你甚至不知道你要多少可调用,使你可以创建可调用的集合,然后做

List<Future<SomeType>> results = pool.invokeAll(collection) 

这将使你的结果更容易处理。当决定是否使用线程池时,可能最重要的事情是如果在线程仍在运行时程序终止,该怎么办;你必须完成你的线程做什么,你需要有错误等

退房的Java线程池的文档的无缝处理:https://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html 或在网络上搜索,有吨的约职位/博客何时或不使用线程池。

但似乎你是在正确的轨道上!

0

谢谢您的回复!它绝对让我走上正轨。我最终实现这样的:

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) 
      {....... 
在过去

for循环读取我的操作的结果,并把它们写在屏幕上或行为,根据返回结果。

我仍然有几个问题,因为它是不是真的我清楚:

1 - 如果我有20个PC和所有可调用线程提交给池在我的第一个for循环,我把它正确地说只有5个线程将​​被启动(threadpool size = 5),但所有的线程都已经被创建并且处于等待状态,并且只有当第一次运行的线程完成并且返回结果值时,下一个线程将自动开始,直到所有PC完成了吗?

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

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

+0

太棒了,这正是我心中所想的,做得很好!至于你的问题: 1:当你创建一个大小为5的固定线程池时,无论你有没有将5个任务放到池中,都会启动5个线程。如果超过5个,他们只需等待,如你所说。 2:invokeAll只是一个建议,有更多的控制权。与submit不同,invokeAll阻塞,直到所有任务完成。这是因为你似乎有这么几项任务,但如果你有更多的任务,你应该考虑使用提交或invokeAll的时间约束变体来处理结果。 – PNS

+0

Hy我还有一个问题,如果我希望在每个线程完成后立即获得一个结果,而不等待池中的所有线程完成,那么实现此目标的最佳方法是什么?我可以告诉每个线程在完成后立即通知我结果吗?谢谢 –