Java語法糖 : 使用 try-with-resources 語句安全地釋放資源

先給出本文的重點:java

  1. 這裏所謂的資源(resource)是指在程序完成後,必須關閉的對象, try-with-resources 語句確保了每一個資源在語句結束時關閉;安全

  2. 使用 Java 7 新增的 try-with-resources 語句 代替 try-finally 語句進行資源關閉,不只代碼更精簡併且更安全;app

  3. 支持 try-with-resources 語句 的類必須都實現 AutoCloseable接口,一樣的,咱們自定義的類也能夠實現這個接口來幫助咱們進行一些安全的自動化釋放資源;ide

  4. Java 9 對 try-with-resources 語句進行了改進,若是你有一個資源是 final 或等效於 final 變量, 則能夠在 try-with-resources 語句中使用該變量,無需在 try-with-resources 語句中再聲明一個新的變量。post

下面就經過幾個簡單而實用的例子,給你們演示一下 try-with-resources 語句的各類用法。性能


Java 7 以前的 try-finally 語句

以前操做資源,爲了防止由於異常形成沒法關閉資源,都是經過 try-finally 語句來關閉資源流的。spa

這樣作有兩個弊端:線程

  1. 代碼醜陋
  2. 不安全

例以下面讀寫文件的一個方法,須要定義大量的變量,以及反覆的異常捕捉和close操做。code

public static void method1() {

    FileWriter fileWriter = null;
    BufferedWriter bufferedWriter = null;
    FileReader fileReader = null;
    BufferedReader bufferedReader = null;
    File file = new File("try-with-resources-demo.txt");

    try {


        fileWriter = new FileWriter(file);
        bufferedWriter = new BufferedWriter(fileWriter);

        fileReader = new FileReader(file);
        bufferedReader = new BufferedReader(fileReader);

        bufferedWriter.write("now is:" + LocalDateTime.now() + "\n\r");
        bufferedWriter.write("availableProcessors are : " + Runtime.getRuntime().availableProcessors() + "\n\r");
        bufferedWriter.write("totalMemory is : " + Runtime.getRuntime().totalMemory() + "\n\r");
        bufferedWriter.write("maxMemory is : " + Runtime.getRuntime().maxMemory() + "\n\r");
        bufferedWriter.write("freeMemory is : " + Runtime.getRuntime().freeMemory() + "\n\r");
        bufferedWriter.flush();

        StringBuffer readResult = new StringBuffer("");
        String oneLine = null;
        while (null != (oneLine = bufferedReader.readLine())) {
            readResult.append(oneLine + "\n\r");
        }
        System.out.println(readResult.toString());


    } catch (IOException ioe) {
        //TODO log: IOException
        ioe.printStackTrace();
    } finally {
        try {
            if (null != fileReader)
                fileReader.close();
        } catch (IOException ioe) {
            //TODO log: close stream has an IOException
            ioe.printStackTrace();
        }

        try {
            if (null != bufferedReader)
                bufferedReader.close();
        } catch (IOException ioe) {
            //TODO log: close stream has an IOException
            ioe.printStackTrace();
        }

        try {
            if (null != fileWriter)
                fileWriter.close();
        } catch (IOException ioe) {
            //TODO log: close stream has an IOException
            ioe.printStackTrace();
        }

        try {
            if (null != bufferedWriter)
                bufferedWriter.close();
        } catch (IOException ioe) {
            //TODO log: close stream has an IOException
            ioe.printStackTrace();
        }

    }
}
複製代碼

這樣的程序,顯然不是有代碼潔癖的小夥伴能夠接受的。orm

try-with-resources 語句

而使用 try-with-resources 語句實現的話,就簡潔了不少。

public static void method2() {
        File file = new File("try-with-resources-demo.txt");
        try (
                FileWriter fileWriter = new FileWriter(file);
                BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
                FileReader fileReader = new FileReader(file);
                BufferedReader bufferedReader = new BufferedReader(fileReader);
        ) {
            bufferedWriter.write("now is:" + LocalDateTime.now() + "\n\r");
            bufferedWriter.write("availableProcessors are : " + Runtime.getRuntime().availableProcessors() + "\n\r");
            bufferedWriter.write("totalMemory is : " + Runtime.getRuntime().totalMemory() + "\n\r");
            bufferedWriter.write("maxMemory is : " + Runtime.getRuntime().maxMemory() + "\n\r");
            bufferedWriter.write("freeMemory is : " + Runtime.getRuntime().freeMemory() + "\n\r");
            bufferedWriter.flush();


            StringBuffer readResult = new StringBuffer("");
            String oneLine = null;
            while (null != (oneLine = bufferedReader.readLine())) {
                readResult.append(oneLine + "\n\r");
            }
            System.out.println(readResult.toString());

        } catch (IOException ioe) {
            //TODO log: IOException
            ioe.printStackTrace();
        }
    }


複製代碼

實現 AutoCloseable 接口

跟蹤源碼你會發現,使用 try-with-resources 語句自動關閉資源的類都實現了AutoCloseable 接口。

AutoCloseable 接口只有一個無參的close()方法,使用try-with-resources 語句聲明的資源,只要實現了這個方法,就能夠在拋出異常以前,調用close()方法進行資源關閉操做。

下面提供了一個使用線程池來執行任務的ExecutorServiceAutoCloseable,這個類實現了 AutoCloseable 接口的close()方法,能夠在異常拋出之後,關閉線程池,從而達到釋放線程資源的目的。

package net.ijiangtao.tech.designskill.trywithresources;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/** * AutoCloseable Thread pool * * @author ijiangtao * @create 2019-05-13 13:08 **/
public class ExecutorServiceAutoCloseable implements AutoCloseable {

    private ExecutorService pool;
    private int poolSize;

    public ExecutorServiceAutoCloseable() {
        poolSize = Runtime.getRuntime().availableProcessors();
        pool = Executors.newFixedThreadPool(poolSize);
    }

    public void execute(Runnable runnable) {
        if (pool.isShutdown())
            throw new UnsupportedOperationException("pool isShutdown now");
        pool.execute(runnable);
    }

    @Override
    public void close() throws Exception {
        System.out.println("auto close now !!!!!!!!!!! ");
        pool.shutdown();
    }

    public static void main(String[] args) {
        try (ExecutorServiceAutoCloseable executorServiceAutoCloseable = new ExecutorServiceAutoCloseable();) {
            executorServiceAutoCloseable.execute(new Runnable() {
                @Override
                public void run() {
                    Integer.parseInt("test auto close");
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

複製代碼

下面是輸出的結果,能夠看到在拋出異常以前,先執行了close()方法來關閉資源。

auto close now !!!!!!!!!!! 
Exception in thread "pool-1-thread-1" java.lang.NumberFormatException: For input string: "test auto close"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:580)
	at java.lang.Integer.parseInt(Integer.java:615)
	at net.ijiangtao.tech.designskill.trywithresources.ExecutorServiceAutoCloseable$1.run(ExecutorServiceAutoCloseable.java:39)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

複製代碼

這樣作的目的是,當程序由於異常而結束的時候,不須要顯式地關閉線程池,而能夠自動關閉,從而儘快釋放線程資源,下降內存消耗。

這裏要注意的是,程序結束就關閉線程池,這樣作的好處是佔用內存小,壞處是每次執行程序都要從新建立線程池,這是有必定性能消耗的。

所以要根據具體狀況而定。一般,若是程序運行頻繁,則保留線程池中線程,反覆使用,減小因反覆建立和銷燬線程形成的性能消耗。而若是程序運行結束之後,短期內不會再次運行,則能夠將線程池關閉,釋放掉佔用的內存。

固然也能夠經過設置核心線程數和最大線程數,以及過時時間來設置本身的線程管理策略。

具體用法能夠參考這篇文章:使用ThreadPoolExecutor構造線程池

Java 9 final 變量

在Java 9 中,對 try-with-resources 語句的語法進行了進一步的精簡。

若是你有一個資源是 final 或等效於 final 變量, 那麼你能夠在 try-with-resources 語句中使用該變量,而無需在 try-with-resources 語句中聲明一個新變量。

Java 7 和 Java 8 中的寫法:

private static String readDataJava7(String message) throws IOException {
    Reader reader = new StringReader(message);
    BufferedReader bufferedReader = new BufferedReader(reader);
    try (BufferedReader bufferedReader2 = bufferedReader) {
        return bufferedReader2.readLine();
    }
}

複製代碼

Java 9 支持的寫法:

private static String readDataJava9(String message) throws IOException {
        Reader reader = new StringReader(message);
        BufferedReader bufferedReader = new BufferedReader(reader);
        try (bufferedReader) {
            return bufferedReader.readLine();
        }
    }
複製代碼

總結

經過 try-with-resources 語句的的好處能夠總結以下:

  1. try-with-resources 語句能夠帶來更加簡潔的代碼
  2. try-with-resources 語句可使得資源釋放更加安全
  3. 本身實現 AutoCloseable 接口並使用 try-with-resources 語句能夠實現安全簡潔的資源釋放

Wechat-westcall
相關文章
相關標籤/搜索