最近有一個需求,需要用一個java進程啟動多個子進程來完成並發任務。由於必須給用戶完成任務的反饋,所以需要父進程記錄子進程的生命周期。
exec方法返回一個Process對象,在當前進程內調用該對象的waitFor方法,然后父進程就會在該方法阻塞,那么只有在該Process結束的情況下,才會從waitFor中返回。
我寫了兩個類來測試:
一個是Father類:
public class Father { private static int count = 0; private static int total = 3; private static String target = "./hell.jar"; private static List<Process> child = new ArrayList<Process> (); public static void main (String[] args) { Runtime run = Runtime.getRuntime(); System.out.println("wait.."); for (int i = 0; i < total; i ++) { try { Process num = run.exec("java -jar " + target); child.add(num); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (Process item : child) { try { item.waitFor(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("All work finished!"); } }
hell.jar由子類son導出,具體代碼如下:
public class Son { public static void main (String[] args) { for (int i = 0; i < 10000; i ++) { System.out.println(i); } System.exit(0); } }
可是,可是!
明明應該很快結束,結果整整卡了15分有余。。
后來上網查了,才發現這是個坑
文檔中如此寫道:
Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock.
這意思簡單概括為:緩沖區如果滿了,爺就掛起來
換言之,子進程把寫往stdout的緩沖區占滿了,所以子進程掛起了,於是父進程就要消逝在永久的等待中了。
了解了原因,解決辦法就簡單了:消耗掉輸出。
我的辦法是重定向。
但是對exec重定向似乎也有坑。。Goolge了一個解決辦法,具體代碼如下:
public class Father { private static int count = 0; private static int total = 3; private static String target = "./hell.jar"; private static List<Process> child = new ArrayList<Process> (); public static void main (String[] args) { Runtime run = Runtime.getRuntime(); System.out.println("wait.."); for (int i = 0; i < total; i ++) { try { Process num = run.exec("cmd /c java -jar " + target + " 1>>1.txt 2>&1"); // 把stdout和stderr的輸出重定向到1.txt中 child.add(num); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (Process item : child) { try { item.waitFor(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("All work finished!"); } }
然后就成功了。
感想:
bat真丑,windows上java的坑真多