- 一個JVM進程對應一個JAVA程序
- Java編寫程序都運行在在Java虛擬機(JVM)中,在JVM的內部,程序的多任務是通過線程來實現的。每用java命令啓動一個java應用程序,就會啓動一個JVM進程。在同一個JVM進程中,有且只有一個進程,就是它自己。在這個JVM環境中,所有程序代碼的運行都是以線程來運行。
- 法一:
- Runtime rt = Runtime.getRuntime();
- Process process = rt.exec("java com.test.process.MyClass");
- 法二:
- ProcessBuilder pb = new ProcessBuilder(myXXcommand,myXXobject);
- Process p = pb.start();
- 注意:
- 建議使用法二進行創建,因爲相對於法一這裏提供了更多的可以設置的東西,如環境變量,工作目錄等;
- 關於創建ProcessBuilder對象是所使用的字符串command的含義:
- command指令其實就是指在當前操作系統中如何啓動一個應用的指令;比如下面的指令
String myQQcommand = "E:\\Program Files (x86)\\Tencent\\QQ\\Bin\\QQ.exe"; //啓動windows下面的qq程序 String myNotePadcommand = "E:\\Program Files\\Notepad++\\notepad++.exe"; //啓動windows下的notepad程序 String myNotePadobject= "C:\\Users\\evan\\Desktop\\files.txt";//notepad程序用於打開的應用 String myJavacommand = "java";//運行java指令 String myJavaobject = "com.test.process.MyClass";//指定Java運行的類對象
- 環境:是從變量到值的依賴於系統的映射。初始值是當前進程環境的一個副本(請參閱 System.getenv())。
- 工作目錄:默認值是當前進程的當前工作目錄,通常根據系統屬性 user.dir 來命名。
- redirectErrorStream 屬性:最初,此屬性爲 false,意思是子進程的標準輸出和錯誤輸出被髮送給兩個獨立的流,這些流可以通過 Process.getInputStream() 和 Process.getErrorStream() 方法來訪問。如果將值設置爲 true,標準錯誤將與標準輸出合併。這使得關聯錯誤消息和相應的輸出變得更容易。在此情況下,合併的數據可從 Process.getInputStream() 返回的流讀取,而從 Process.getErrorStream() 返回的流讀取將直接到達文件尾
- 進程間通信的方法有:
- 管道( pipe ):管道是一種半雙工的通信方式,數據只能單向流動,而且只能在具有親緣關係的進程間使用。進程的親緣關係通常是指父子進程關係。
- 套接字( socket ) : 套接字也是一種進程間通信機制,與其他通信機制不同的是,它可用於不同及其間的進程通信。
- 父進程實現:
ServerSocket s = new ServerSocket(int port); Socket incoming = s.accept(); InputStream inStream = incoming.getInputStream(); OutputStream outStream = incoming.getOutputStream(); ...//讀取操作,一般還會對InputStream和OutputStream分別用Scanner和PrintWriter進行一下包裝 incoming.close();
- 子進程實現
Socket s = new Socket("127.0.0.1"",port); InputStream inStream = s.getInputStream(); OutputStream outStream = s.getOutputStream(); ....//讀取操作,一般還會對InputStream和OutputStream分別用Scanner和PrintWriter進行一下包裝
- 父進程實現:
- 共享內存( shared memory ) :共享內存就是映射一段能被其他進程所訪問的內存,這段共享內存由一個進程創建,但多個進程都可以訪問。共享內存是最快的 IPC 方式,它是針對其他進程間通信方式運行效率低而專門設計的。它往往與其他通信機制,如信號量,配合使用,來實現進程間的同步和通信。
- 信號量( semophore ) : 信號量是一個計數器,可以用來控制多個進程對共享資源的訪問。它常作爲一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。因此,主要作爲進程間以及同一進程內不同線程之間的同步手段。
- 消息隊列( message queue ) : 消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識。消息隊列克服了信號傳遞信息少、管道只能承載無格式字節流以及緩衝區大小受限等缺點。
- 信號 ( sinal ) : 信號是一種比較複雜的通信方式,用於通知接收進程某個事件已經發生。
- 管道方式(JAVA多進程間使用最簡單)
- 父進程獲取子進程輸出流方式
BufferedInputStream in = new BufferedInputStream(p.getInputStream()); //p是前面創建的子進程 BufferedReader br = new BufferedReader(new InputStreamReader(in)); String s; while ((s = br.readLine()) != null) { System.out.println(s); }
- 父進程獲取子進程輸入流方式
- OutputStream ops = p.getOutputStream();
- ops.write(b);
- 子進程獲取父進程輸入輸出流方式
- 子進程通過System.in得到的數據並不是從控制檯輸入的數據,而是父進程通過一個outputStream寫入的數據;
- 子進程通過System.out輸出的數據並不是輸出到控制檯,而是被父進程通過一個intputStream讀取;
package com.test.process; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class T3 { public static void main(String[] args) throws IOException { System.out.println("子進程被調用成功!"); BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in)); while (true) { String strLine = bfr.readLine(); if (strLine != null) { System.out.println("hi:" + strLine); } } } }
- 管道通信方式總結如下:
- 父進程通過Process.getOutputStream()和Process.getInputStream()來獲得子進程的輸入輸出流;
- 子進程通過System.in、System.out來獲取父進程的輸入輸出流;
- 父進程獲取子進程輸出流方式
父進程測試類:
package com.test.process.pipe;
import java.io.IOException;
public class ProcessTest {
static void main(String[] args) throws IOException, InterruptedException {
Process p = Runtime.getRuntime().exec("java com.test.process.pipe.MyTest");
StringBuilder sbuilder = new StringBuilder();
for(int k=0;k<1;k++){
sbuilder.append("hello");
}
int outSize = 1;
TestOut out[] = new TestOut[outSize];
for(int i=0;i<outSize;i++){
out[i] = new TestOut(p,sbuilder.toString().getBytes());
new Thread(out[i]).start();
}
int inSize = 1;
TestIn in[] = new TestIn[inSize];
for(int j=0;j<inSize;j++){
in[j] = new TestIn(p);
new Thread(in[j]).start();
}
}
}
子進程測試類
package com.test.process.pipe; import java.io.BufferedReader; import java.io.InputStreamReader; public class MyTest { public static void main(String[] args) throws Exception { //讀取父進程輸入流 BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in)); while (true) { String strLine = bfr.readLine(); if (strLine != null) { System.out.println(strLine);//這個地方的輸出在子進程控制檯是無法輸出的,只可以在父進程獲取子進程的輸出 }else { return; } } } }
TestIn類
package com.test.process.pipe; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; public class TestIn implements Runnable{ private Process p = null; public TestIn(Process process){ p = process; } @Override public void run() { try { InputStream in = p.getInputStream(); BufferedReader bfr = new BufferedReader(new InputStreamReader(in)); String rd = bfr.readLine(); if(rd != null){ System.out.println(rd);//輸出子進程返回信息(即子進程中的System.out.println()內容) }else{ return; } //注意這個地方,如果關閉流則子進程的返回信息無法獲取 //bfr.close(); //in.close(); } catch (Exception e) { e.printStackTrace(); } } }
TestOut類
package com.test.process.pipe; import java.io.IOException; import java.io.OutputStream; public class TestOut implements Runnable { private Process p = null; private byte []b = null; public TestOut(Process process,byte byt[]){ p = process; b = byt; } @Override public void run() { try { OutputStream ops = p.getOutputStream(); //System.out.println("out--"+b.length); ops.write(b); //注意這個地方如果關閉,則父進程只可以給子進程發送一次信息,如果這個地方開啓close()則父進程給子進程不管發送大小多大的數據,子進程都可以返回 //如果這個地方close()不開啓,則父進程給子進程發送數據累加到8192子進程才返回。 //ops.close(); } catch (IOException e) { e.printStackTrace(); } } }
備註:
1、子進程的輸出內容是無法在控制檯輸出的,只能再父類中獲取並輸出。2、父進程往子進程寫內容時如果關閉字節流,則子進程的輸入流同時關閉。
3、如果父進程中輸入、輸出流都不關閉,子進程獲取的字節流在達到8129byte時才返回。
4、關閉子進程一定要在父進程中關閉 p.destroy()
/** *如下另一種情況說明 *如果像如下情況執行會出現說明情況呢 *前提說明:TestOut類中開啓ops.close(); */ package com.test.process.pipe; import java.io.IOException; public class ProcessTest { public static void main(String[] args) throws IOException, InterruptedException { Process p = Runtime.getRuntime().exec("java com.test.process.pipe.MyTest"); TestOut out = new TestOut(p,"Hello everyone".getBytes()); new Thread(out).start(); TestIn ti = new TestIn(p); new Thread(ti).start(); Thread.sleep(3000); TestOut out2 = new TestOut(p,"-Hello-everyone".getBytes()); new Thread(out2).start(); TestIn ti2 = new TestIn(p); new Thread(ti2).start(); }執行後輸出結果爲:
Hello everyone java.io.IOException: Stream closed at java.io.BufferedInputStream.getBufIfOpen(BufferedInputStream.java:145 ) at java.io.BufferedInputStream.read(BufferedInputStream.java:308) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158) at java.io.InputStreamReader.read(InputStreamReader.java:167) at java.io.BufferedReader.fill(BufferedReader.java:136) at java.io.BufferedReader.readLine(BufferedReader.java:299) at java.io.BufferedReader.readLine(BufferedReader.java:362) at com.test.process.pipe.TestIn.run(TestIn.java:20) at java.lang.Thread.run(Thread.java:662)由此可見當創建一個子進程後,p.getOutputStream();p.getInputStream();通過兩種方式使父進程與子進程建立管道連接,而當close()連接時管道關閉,再通過調用p.getOutputStream();p.getInputStream();時直接出現IOException,結論爲當父子進程建立連接後,通過管道長連接的方式進程信息傳輸,當close時在通過獲取子進程的輸入輸出流
都會出現IOException;
package com.test.process.pipe; import java.io.IOException; public class ProcessTest { public static void main(String[] args) throws IOException, InterruptedException { Process p = Runtime.getRuntime().exec("java com.test.process.pipe.MyTest"); TestOut out = new TestOut(p,"Hello everyone".getBytes()); new Thread(out).start(); TestIn ti = new TestIn(p); new Thread(ti).start(); Process p2 = Runtime.getRuntime().exec("java com.test.process.pipe.MyTest"); TestOut out2 = new TestOut(p2,"-Hello-everyone".getBytes()); new Thread(out2).start(); TestIn ti2 = new TestIn(p2); new Thread(ti2).start(); }輸出結果:
Hello everyone -Hello-everyone綜上可見每個父進程創建一個子進程後,通過p.getOutputStream();p.getInputStream();建立管道連接後,無法關閉流,如果關閉了則需要重新建立進程纔可以達到通信的效果。 如果不關閉流,則傳輸的字符內容累加到8192byte時纔可以返回。