前一篇文章讲了线程池的execute方法,介绍了线程池的执行原理与两种提交任务执行(execute()和submit())的差异以及线程池执行任务出现异常的解决方案。然而线程池里面复用的是线程资源,而线程是系统资源的一种。所以关闭线程池是为了正确地终止线程池的运行并释放相关资源。下面是关闭线程池的重要原因:
释放资源:线程池内部会创建一定数量的线程以及其他相关资源,如线程队列、线程池管理器等。如果不及时关闭线程池,这些资源将一直占用系统资源,可能导致内存泄漏或资源浪费。
防止任务丢失:线程池中可能还有未执行的任务,如果不关闭线程池,这些任务将无法得到执行。关闭线程池时,会等待所有已提交的任务执行完毕,确保任务不会丢失。
优雅终止:关闭线程池可以让线程池中的线程正常执行完当前任务后停止,避免突然终止线程导致的资源释放不完整或状态不一致的问题。
避免程序阻塞:在某些情况下,如果不关闭线程池,程序可能会一直等待线程池中的任务执行完毕,从而导致程序阻塞,无法继续执行后续的逻辑。
因此,为了正确管理系统资源、避免任务丢失、保证程序的正常执行和避免阻塞,应当在不再需要线程池时及时关闭它。关闭线程池的一般做法是调用线程池的shutdown()方法,它会优雅地关闭线程池,等待已提交的任务执行完毕后才会终止线程池的运行。
线程关闭的方法有很多
shutdown()是线程池正常关闭的方法,它会先停止接收新的任务,然后等待已经提交的任务执行完毕后再停止。调用shutdown()后,线程池会逐渐停止,但不会立即停止。当线程池中的任务都执行完毕后,shutdown()会将所有的线程都关闭,这时线程池就终止了。
isShutdown()返回线程池是否已经调用过shutdown(),如果已经调用过,则返回true,否则返回false。
isTerminated()用于判断线程池中的所有任务是否已经执行完毕,并且所有线程都已经被关闭。如果是,则返回true,否则返回false。
awaitTermination()用于等待线程池中的任务执行完毕并关闭线程池。它会阻塞调用线程,直到线程池中的所有任务都执行完毕或者等待超时。该方法需要传入一个超时时间和时间单位,如果超时了,就会返回false,否则返回true。
shutdownNow()是强制关闭线程池的方法,它会尝试立即停止正在执行的任务,并返回等待执行的任务列表。调用shutdownNow()后,线程池会立即停止,但不保证所有正在执行的任务都能被停止。
需要注意的是,调用shutdownNow()会抛出InterruptedException异常,需要进行异常处理。并且,在使用shutdownNow()强制关闭线程池时,需要确保所有任务都能够正常停止,否则可能会导致任务数据丢失或其他问题。
线程池使用完毕为何必须shutdown()方法
首先,线程池执行线程时也是通过Thread对象的start()来启动线程,这种方式的线程本身就会占用一个虚拟机栈,而虚拟机栈在JVM中属于GC Roots。
根据可达性分析算法,这个线程就不可能被回收。一直占用JVM的内存资源。这样就会造成一个问题,线程池如果没有执行shutdown或shutdownnow。
那么构建的所有核心线程就永远不能被回收,这样就会造成内存泄漏问题。除了线程内存的泄漏还有另外一个问题,线程池启动线程是基于Worker内部的Thread去启动的,当执行t.start之后,它会执行worker的run方法,接着调用runworker方法,而runworker方法的传入的是this就是当前的worker对象。
那么可以这样理解,我启动一个线程还指向Worker对象。那么worker对象也是不能被回收的,同时worker对象时线程池的内部类,就会出现内部类都不能被回收,那外部类整个线程池也不能被回收。