热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java语法糖:使用trywithresources语句安全地释放资源

先给出本文的重点:这里所谓的资源(resource)是指在程序完成后,必须关闭的对象,try-with-resources语

先给出本文的重点:

  1. 这里所谓的资源(resource)是指在程序完成后,必须关闭的对象, try-with-resources 语句确保了每个资源在语句结束时关闭;

  2. 使用 Java 7 新增的 try-with-resources 语句 代替 try-finally 语句进行资源关闭,不仅代码更精简而且更安全;

  3. 支持 try-with-resources 语句 的类必须都实现 AutoCloseable接口,同样的,我们自定义的类也可以实现这个接口来帮助我们进行一些安全的自动化释放资源;

  4. Java 9 对 try-with-resources 语句进行了改进,如果你有一个资源是 final 或等效于 final 变量, 则可以在 try-with-resources 语句中使用该变量,无需在 try-with-resources 语句中再声明一个新的变量。

下面就通过几个简单而实用的例子,给大家演示一下 try-with-resources 语句的各种用法。


Java 7 之前的 try-finally 语句

之前操作资源,为了防止因为异常造成无法关闭资源,都是通过 try-finally 语句来关闭资源流的。

这样做有两个弊端:

  1. 代码丑陋
  2. 不安全

例如下面读写文件的一个方法,需要定义大量的变量,以及反复的异常捕捉和close操作。

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: IOExceptionioe.printStackTrace();} finally {try {if (null != fileReader)fileReader.close();} catch (IOException ioe) {//TODO log: close stream has an IOExceptionioe.printStackTrace();}try {if (null != bufferedReader)bufferedReader.close();} catch (IOException ioe) {//TODO log: close stream has an IOExceptionioe.printStackTrace();}try {if (null != fileWriter)fileWriter.close();} catch (IOException ioe) {//TODO log: close stream has an IOExceptionioe.printStackTrace();}try {if (null != bufferedWriter)bufferedWriter.close();} catch (IOException ioe) {//TODO log: close stream has an IOExceptionioe.printStackTrace();}}
}

这样的程序,显然不是有代码洁癖的小伙伴可以接受的。

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: IOExceptionioe.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);}@Overridepublic 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() {@Overridepublic 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 语句可以实现安全简洁的资源释放


转:https://juejin.im/post/5cd9434ce51d45473d10ff03



推荐阅读
  • 字节流(InputStream和OutputStream),字节流读写文件,字节流的缓冲区,字节缓冲流
    字节流抽象类InputStream和OutputStream是字节流的顶级父类所有的字节输入流都继承自InputStream,所有的输出流都继承子OutputStreamInput ... [详细]
  • 零拷贝技术是提高I/O性能的重要手段,常用于Java NIO、Netty、Kafka等框架中。本文将详细解析零拷贝技术的原理及其应用。 ... [详细]
  • Hadoop的文件操作位于包org.apache.hadoop.fs里面,能够进行新建、删除、修改等操作。比较重要的几个类:(1)Configurati ... [详细]
  • C#实现文件的压缩与解压
    2019独角兽企业重金招聘Python工程师标准一、准备工作1、下载ICSharpCode.SharpZipLib.dll文件2、项目中引用这个dll二、文件压缩与解压共用类 ... [详细]
  • Spring – Bean Life Cycle
    Spring – Bean Life Cycle ... [详细]
  • oracle c3p0 dword 60,web_day10 dbcp c3p0 dbutils
    createdatabasemydbcharactersetutf8;alertdatabasemydbcharactersetutf8;1.自定义连接池为了不去经常创建连接和释放 ... [详细]
  • 属性类 `Properties` 是 `Hashtable` 类的子类,用于存储键值对形式的数据。该类在 Java 中广泛应用于配置文件的读取与写入,支持字符串类型的键和值。通过 `Properties` 类,开发者可以方便地进行配置信息的管理,确保应用程序的灵活性和可维护性。此外,`Properties` 类还提供了加载和保存属性文件的方法,使其在实际开发中具有较高的实用价值。 ... [详细]
  • 本文介绍了如何将包含复杂对象的字典保存到文件,并从文件中读取这些字典。 ... [详细]
  • JVM钩子函数的应用场景详解
    本文详细介绍了JVM钩子函数的多种应用场景,包括正常关闭、异常关闭和强制关闭。通过具体示例和代码演示,帮助读者更好地理解和应用这一机制。适合对Java编程和JVM有一定基础的开发者阅读。 ... [详细]
  • 本文详细介绍了Java反射机制的基本概念、获取Class对象的方法、反射的主要功能及其在实际开发中的应用。通过具体示例,帮助读者更好地理解和使用Java反射。 ... [详细]
  • DAO(Data Access Object)模式是一种用于抽象和封装所有对数据库或其他持久化机制访问的方法,它通过提供一个统一的接口来隐藏底层数据访问的复杂性。 ... [详细]
  • 本文介绍了在 Java 编程中遇到的一个常见错误:对象无法转换为 long 类型,并提供了详细的解决方案。 ... [详细]
  • 在分析Android的Audio系统时,我们对mpAudioPolicy->get_input进行了详细探讨,发现其背后涉及的机制相当复杂。本文将详细介绍这一过程及其背后的实现细节。 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • Java Socket 关键参数详解与优化建议
    Java Socket 的 API 虽然被广泛使用,但其关键参数的用途却鲜为人知。本文详细解析了 Java Socket 中的重要参数,如 backlog 参数,它用于控制服务器等待连接请求的队列长度。此外,还探讨了其他参数如 SO_TIMEOUT、SO_REUSEADDR 等的配置方法及其对性能的影响,并提供了优化建议,帮助开发者提升网络通信的稳定性和效率。 ... [详细]
author-avatar
Susan林小夕
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有