1. 程式人生 > >Java中RunTime類介紹

Java中RunTime類介紹

tro 源碼 new dex notepad 拷貝 spa 瀏覽器 int

Runtime 類代表著Java程序的運行時環境,每個Java程序都有一個Runtime實例,該類會被自動創建,我們可以通過Runtime.getRuntime() 方法來獲取當前程序的Runtime實例。

獲取當前Jvm的內存信息

/*
 * 獲取當前jvm的內存信息,返回的值是 字節為單位
 * */
public static void getFreeMemory() {
    //獲取可用內存
    long value = Runtime.getRuntime().freeMemory();
    System.out.println("可用內存為:"+value/1024/1024+"mb");
    
//獲取jvm的總數量,該值會不斷的變化 long totalMemory = Runtime.getRuntime().totalMemory(); System.out.println("全部內存為:"+totalMemory/1024/1024+"mb"); //獲取jvm 可以最大使用的內存數量,如果沒有被限制 返回 Long.MAX_VALUE; long maxMemory = Runtime.getRuntime().maxMemory(); System.out.println("可用最大內存為:"+maxMemory/1024/1024+"mb"); }

獲取jvm可用的處理器核心的數量

一般可以和newFixedThreadPool一起使用

/*
 * 獲取jvm可用的處理器核心的數量
 * */
public static void getAvailableProcessors() {
    int value = Runtime.getRuntime().availableProcessors();
    System.out.println(value);
   ExecutorService fixedThreadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); }

執行系統命令

RunTime.getRuntime().exec()的構造方法

public Process exec(String command)-----在單獨的進程中執行指定的字符串命令。
public Process exec(String [] cmdArray)---在單獨的進程中執行指定命令和變量
public Process exec(String command, String [] envp)----在指定環境的獨立進程中執行指定命令和變量
public Process exec(String [] cmdArray, String [] envp)----在指定環境的獨立進程中執行指定的命令和變量
public Process exec(String command,String[] envp,File dir)----在有指定環境和工作目錄的獨立進程中執行指定的字符串命令
public Process exec(String[] cmdarray,String[] envp,File dir)----在指定環境和工作目錄的獨立進程中執行指定的命令和變量

Process的幾種方法

1.destroy():殺掉子進程
2.exitValue():返回子進程的出口值,值 0 表示正常終止
3.getErrorStream():獲取子進程的錯誤流
4.getInputStream():獲取子進程的輸入流
5.getOutputStream():獲取子進程的輸出流
6.waitFor():導致當前線程等待,如有必要,一直要等到由該Process對象表示的進程已經終止。如果已終止該子進程,此方法立即返回。如果沒有終止該子進程,調用的線程將被阻塞,直到退出子進程,根據慣例,0 表示正常終止

註意:在java中,調用runtime線程執行腳本是非常消耗資源的,所以切忌不要頻繁使用!

在調用runtime去執行腳本的時候,其實就是JVM開了一個子線程去調用JVM所在系統的命令,其中開了三個通道:輸入流、輸出流、錯誤流,其中輸出流就是子線程走調用的通道。
大家都知道,waitFor是等待子線程執行命令結束後才執行, 但是在runtime中,打開程序的命令如果不關閉,就不算子線程結束。比如以下代碼。

private static Process p = null;
p = Runtime.getRuntime().exec("notepad.exe");
p.waitFor();         
System.out.println("-------------------我被執行了-------------------");

以上代碼中,打開windows中記事本。如果我們不手動關閉記事本,那麽輸出語句就不會被執行,這點是需要理解的。

process的阻塞

在runtime執行大點的命令中,輸入流和錯誤流會不斷有流進入存儲在JVM的緩沖區中,如果緩沖區的流不被讀取被填滿時,就會造成runtime的阻塞。所以在進行比如:大文件復制等的操作時,我們還需要不斷的去讀取JVM中的緩沖區的流,來防止Runtime的死鎖阻塞。 代碼:linux中拷貝文件防止阻塞的寫法 技術分享圖片

技術分享圖片

打開記事本

Runtime.getRuntime().exec("notepad.exe");

打開某個文件(不管是word還excel等等文件)

Runtime.getRuntime().exec("cmd /c start " + "\"\" \"" + path + "\"");

打開ie瀏覽器

Runtime.getRuntime().exec("C:\\Program Files\\Internet Explorer\\IEXPLORE.EXE");

得到系統的環境變量

@Test
public void dirRuntimeProcess() throws IOException, InterruptedException {
    Process process = Runtime.getRuntime().exec("cmd.exe /c echo %JAVA_HOME%");
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));

    String string = null;
    while ((string = bufferedReader.readLine()) != null) {
        System.out.println(string); // D:\Java\jdk\jdk1.8.0_152
    }
    process.waitFor();
    System.out.println("return: " + process.exitValue()); // return: 0
}

得到java的版本號,這個和上述的不一樣

@Test
public void getJavaVersion() {
    try {
        Process process = Runtime.getRuntime().exec("javac -version");
        BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()));
        String line = null;
        while ((line = br.readLine()) != null)
            System.out.println(line); // javac 1.8.0_152
        process.waitFor();
        System.out.println("Process exitValue: " + process.exitValue());
    } catch (Throwable t) {
        t.printStackTrace();
    }
}

執行外部命令得到的結果

@Test
public void execProgramC() {
    try {
        Process process = Runtime.getRuntime().exec("C:/Users/76801/Desktop/huhx.exe");
        BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line = null;
        while ((line = br.readLine()) != null)
            System.out.println(line); // Hello World.
        process.waitFor();
        System.out.println("Process exitValue: " + process.exitValue());
    } catch (Throwable t) {
        t.printStackTrace();
    }
}

huhx.c比較簡單,就是打印一句話。

#include<stdio.h>

void main() {
    printf("Hello World.");
}

導出mysql腳本

@Test
public void execMysqldump() throws IOException, InterruptedException {
    String execCommand = "cmd c/ D:/Java/mysqldump.exe -uhuhx -phuhx boot_learn > D:/bootlearn.sql";
    System.out.println("exec command: " + execCommand);
    Runtime runtime = Runtime.getRuntime();
    Process p = runtime.exec(execCommand);
    StreamGobbler errorGobbler = new StreamGobbler(p.getErrorStream(), "Error");
    StreamGobbler outputGobbler = new StreamGobbler(p.getInputStream(), "Output");
    errorGobbler.start();
    outputGobbler.start();
    p.waitFor();
    System.out.println("successful." + p.exitValue());
}

上述也使用到了網上所說的讀出窗口的標準輸出緩沖區中的內容,仍舊沒有解決Process的waitFor阻塞問題。下面是清空緩沖區的線程代碼:

public class StreamGobbler extends Thread {

    InputStream is;
    String type;

    public StreamGobbler(InputStream is, String type) {
        this.is = is;
        this.type = type;
    }

    public void run() {
        try (InputStreamReader isr = new InputStreamReader(is);) {
            BufferedReader br = new BufferedReader(isr);
            String line = null;
            while ((line = br.readLine()) != null) {
                if (type.equals("Error")) {
                    System.out.println("Error   :" + line);
                } else {
                    System.out.println("Debug:" + line);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代碼的目標是導出mysql數據庫的腳本。沒有找到問題的解決方案,運行環境是win10,jdk1.8。

在JVM中增加一個關閉的鉤子

閱讀ElasticSearch的源碼時,BootStrap類中調用了Runtime.getRuntime().addShutdownHook方法。接下來對java.lang包中的Runtime類的addShutdownHook方法進行說明。

這個方法的意思就是在JVM中增加一個關閉的鉤子,當JVM關閉的時候,會執行系統中已經設置的所有通過方法addShutdownHook添加的鉤子,當系統執行完這些鉤子後,JVM才會關閉。所以這些鉤子可以在JVM關閉的時候進行內存清理、對象銷毀等操作。

為了更清晰的說明,編寫如下程序:

public class TestShutdownHook {
    public static void main(String[] args) {
        // thread1
        Thread thread1 = new Thread() {
            public void run() {
                System.out.println("thread1");
            }
        };
 
        // thread2
        Thread thread2 = new Thread() {
            public void run() {
                System.out.println("thread2");
            }
        };
 
        // shutdownThread
        Thread shutdownThread = new Thread() {
            public void run() {
                System.out.println("shutdownThread");
            }
        };
 
        Runtime.getRuntime().addShutdownHook(shutdownThread);
 
        thread1.start();
        thread2.start();
    }
}

輸出結果:

thread1
thread2
shutdownThread
或
thread2
thread1
shutdownThread

無論是先打印thread1還是thread2,shutdownThread 線程都是最後執行的(因為這個線程是在JVM執行關閉前才會執行)。

關閉JVM程序

Runtime的exit表示的關閉JVM程序,但是不釋放內存,舉例:

System.exit(0);//表示正常程序的關閉,執行的是以下操作:
public static void exit(int status) {
    Runtime.getRuntime().exit(status);
}

備註:如果是 0表示正常關閉,如果是非0表示異常關閉,如果想釋放內存的話,用“dispose()”關閉當前程序,並釋放資源。

System.exit()的內部實現也是Runtime.getRuntime().exit();

Java中RunTime類介紹