2016-01-27 136 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我還有一個問題,如果我希望在每個線程完成後立即獲得一個結果,而不等待池中的所有線程完成,那麼實現此目標的最佳方法是什麼?我可以告訴每個線程在完成後立即通知我結果嗎?謝謝 –