-
ThreadPoolExecutor类可以非常方便的创建线程池对象,而不需要程序员设计大量的
new
去实例化Thread相关的代码-
最常用的构造方法 (不过threadFactory可以视情况设或不设)
ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory )
-
参数解释如下
-
corePoolSize
-
核心线程数,默认情况下核心线程会一直存活,即使处于閒置状态也不会受存keepAliveTime限制 (除非将allowCoreThreadTimeOut设置爲true)
-
-
maximumPoolSize
-
线程池所能容纳的最大线程数,超过这个数的线程将被阻塞
-
但是当任务队列workQueue是一个没有设置大小的
LinkedBlockingDeque
时,这个值无效
-
-
keepAliveTime
-
非核心线程(也就是线程数量大于corePoolSize的线程) 的閒置超时时间,超过这个时间就会被回收
-
-
unit
: keepAliveTime参数的时间单位,像是TimeUnit.SECONDS、TimeUnit.MILLISECONDS.. -
workQueue
-
执行前用于保持任务的队列,此队列仅保持由
pool.execute(Runnable r)
方法提交的Runnable任务 -
常用的有三种队列,分别是LinkedBlockingQueue、ArrayBlockingQueue、SynchronousQueue
-
-
threadFactory
: 线程工厂,可以自定义创建新线程时想添加的功能,像是为每个Thread取名之类的-
ThreadFactory是一个接口,只有一个方法
Thread newThread(Runnable r)
-
当程序员调用
pool.execute(Runnable r)
时,此r
就会经过newThread()
方法,去生成一个Thread
-
-
-
线程池运行的规则
-
线程池的线程执行规则跟任务队列使用哪种BlockingQueue有很大的关系,分为两种不同的BlockingQueue的情况来讨论
-
以下假设 线程数量 代表
pool.execute(Runnable r)
总共需要执行的Runnable任务数量,也就是程序员设置的要执行的任务们的数量
-
-
使用
LinkedBlockingQueue
-
假设 线程数量 <= 核心线程数量
-
那麽直接创建线程去运行这些任务,不会放入队列中,有几个任务就创几条,反正再怎麽创也不会超过核心线程数量就好
-
-
假设 线程数量 > 核心线程数量
-
任务队列 是
LinkedBlockingDeque
,且 有 设置大小限制-
先将超过核心线程的任务放在LinkedBlockingDeque中排队,但是如果LinkedBlockingDeque也给塞满时,多出来的任务会直接创建新线程来执行
-
而当多出来的任务太多,假设使得 总线程数量 > 最大线程数时,就会抛出异常,如果没有超过最大线程数,那就没事
-
要注意,这些新创建的这些线程属于非核心线程,在任务完成后,当閒置时间达到了超时时间就会被清除
-
-
任务队列 是
LinkedBlockingDeque
,且 没有 大小限制-
也是将超过核心线程的任务放在LinkedBlockingDeque中排队
-
但因为没有设置大小限制,所以LinkedBlockingDeque永远不会塞满,所以永远不会有多出来的任务,所以也就不会去创建核心线程之外新的线程,所以他的总线程数量会维持在核心线程数量corePoolSize
-
也就是说当任务队列是LinkedBlockingDeque并且没有大小限制时,线程池的最大线程数设置是无效的,因为他的线程数最多不会超过核心线程数
-
-
-
-
使用
SynchronousQueue
-
假设 线程数量 <= 核心线程数量
-
一样是直接创建几条线程去运行这些任务,不会放入队列中
-
-
假设 线程数量 > 核心线程数量 且 <= 最大线程数
-
线程池会创建新线程执行任务,这些任务也不会被放在SynchronousQueue中
-
这些新创建的线程属于非核心线程,在任务完成后,閒置时间达到了超时时间就会被清除
-
-
假设 线程数量 > 核心线程数量 且 > 最大线程数
-
会因爲线程池拒绝添加任务而抛出异常
-
会报异常是因为SynchronousQueue没有数量限制,不如说他根本不保持这些任务,而是直接交给线程池去执行,所以当任务数量超过最大线程数时才会直接抛异常
-
-
-
-
具体实例
public class Main { public static void main(String[] args) { ExecutorService threadPool = new ThreadPoolExecutor( 5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(1), new NameThreadFactory("mytestPool") ); for(int i=1; i<=7; i++) { threadPool.execute(new Runnable() { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } //取得当初设置的corePoolSize参数 System.out.println("corePoolSize: " + ((ThreadPoolExecutor) threadPool).getCorePoolSize()); //取得目前线程池裡总共有几条线程 System.out.println("currentPoolSize: " + ((ThreadPoolExecutor) threadPool).getPoolSize()); } } class ThreadFactory implements ThreadFactory { private final AtomicInteger mThreadNum; private final String mPrefix; private final boolean mDaemo; private final ThreadGroup mGroup; public NamedThreadFactory(String prefix) { this(prefix, true); } public NamedThreadFactory(String prefix, boolean daemo) { this.mThreadNum = new AtomicInteger(1); this.mPrefix = prefix + "-thread-"; this.mDaemo = daemo; SecurityManager s = System.getSecurityManager(); this.mGroup = s == null ? Thread.currentThread().getThreadGroup() : s.getThreadGroup(); } public Thread newThread(Runnable runnable) { String name = this.mPrefix + this.mThreadNum.getAndIncrement(); Thread ret = new Thread(this.mGroup, runnable, name, 0L); ret.setDaemon(this.mDaemo); return ret; } public ThreadGroup getThreadGroup() { return this.mGroup; } }
mytestPool-thread-1 mytestPool-thread-2 mytestPool-thread-4 mytestPool-thread-5 mytestPool-thread-3 mytestPool-thread-6 corePoolSize: 5 currentPoolSize: 6 //...过了2000ms之后,此时虽然所有Runnable的run都运作完了,但线程池仍会继续运作,程式不会停止 //除非使用shutdown()或shutdownNow()来停止线程池,才会使整个程式关闭
-
-
ThreadPoolExecutor提供的实例方法
-
execute(Runnable r)
: 新增一个Runnable任务到线程池中-
至于此Runnable会马上执行,或是被放到队列中等待,要看此线程池的机制和当下的状况
-
-
shutdown()
: 使当前未执行完的线程继续执行,但不再添加新的任务Task,当所有任务都运行完毕,线程池就会停止-
所有任务指的是 "当前正在运行的任务 + 队列中等的任务",要这两个都运行完,才算运行完毕
-
如果main不调用
shutdown()
方法,就直接结束的话,那麽线程池会一直继续保持运作,以便随时执行被添加的任务,前面的例子就是因为这样,所以程序才不会停止 -
当线程池调用
shutdown()
方法时,线程池的状态会立刻变成 SHUTDOWN 状态,此时不能再往线程池中添加任何任务,否则会抛出RejectedExecutrionException异常,但此时线程池不会立刻退出,他会等到池中所有任务(正在运行的+队列中的)执行完毕之后,才会退出
-
-
List<Runnable> shutdownNow()
: 发出一个interrupt()
中断池子内的所有线程,且未执行的线程不再执行,也就是说从队列中删除-
返回的Runnable list就是没被执行的那些Runnable们
-
不同于上面的
shutdown()
方法,当线程池调用shutdownNow()
方法时,线程池的状态会立刻变成 STOP 状态,并发出interrupt()
试图停止所有正在执行的线程,同时他也不再处理那些还在池队列中等待的任务,取而代之的是他会将这些任务返回-
shundown()
方法会把队列中的任务跑完,但shutdownNow()
不会
-
-
简单的说,
shutdown()
就是不淮再有新任务进来了,但是已经进来的任务我会乖乖把你做完,而shutdownNow()
则是不管任何事,我现在就要立马结束,没完成的任务赶紧回家吧
-
-
isShutdown()
: 判断线程池是否处在shutdown状态-
他其实就是去看线程池的状态,只要状态是 SHUTDOWN 或是 STOP,就返回true
-
-
isTerminating()
: 如果正在执行的线程池已经调用过 shutdown() 或 shutdownNow(),处在正在中止但还没完全结束的过程中,调用此方法返回true -
isTerminated()
: 如果线程池关闭后,此方法返回true -
小结
-
shutdown()
和shutdownNow()
的功能是发出一个关闭大门的命令,而isShutdown()
是判断这个关闭大门的命令发出或是尚未发出,isTerminating()
代表大门是否正在关闭进行中,isTerminated()
判断大门是否已经关闭关好了
-
-
-
具体实例
-
使用
shutdown()
来关闭线程池-
如果Runnable们在sleep过1秒之后,都执行完毕的话,那就能够顺利关掉线程池,程式因此停止
class MyRunnable implements Runnable { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("run 执行完毕"); } } public class Main { public static void main(String[] args) { ExecutorService threadPool = new ThreadPoolExecutor(5, 10, 1, TimeUnit.SECONDS, new LinkedBlockingQueue(1)); threadPool.execute(new MyRunnable()); threadPool.execute(new MyRunnable()); threadPool.shutdown(); System.out.println("shutdown() 线程池"); } }
shutdown() 线程池 run 执行完毕 run 执行完毕 //程序停止了
-
-
但是如果Runnable始终不运行完的话,那麽这个被
shutdown()
的线程池只能一直等他,直到等到有一天他终于执行完毕之后,此线程池才会结束,而程序也才会跟著结束-
在此例中,两个线程都在等待
x.signal()
,所以他们一直处于阻塞状态,始终结束不了,因而线程池也结束不了
class MyRunnable implements Runnable { private final String x = new String(); @Override public void run() { synchronized (x){ try { x.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("run 执行完毕"); } } public class Main { public static void main(String[] args) { ExecutorService threadPool = new ThreadPoolExecutor(5, 10, 1, TimeUnit.SECONDS, new LinkedBlockingQueue(1)); threadPool.execute(new MyRunnable()); threadPool.execute(new MyRunnable()); //使用shutdown()关闭线程池 threadPool.shutdown(); System.out.println("shutdown() 线程池"); } }
shutdown() 线程池 //程序仍在运行...
-
-
如果改使用
shutdownNow()
来结束线程池,那麽因为他会抛出一个InterruptException,所以可以解决因为wait()
、sleep()
、join()
这种被阻塞的线程的等待-
两个线程在
x.wait()
处等待,线程进入阻塞状态,而当main线程执行shutdownNow()
时,线程池会对他池子裡面的所有的线程发出一个interrupt()
,因此他们这两个runnable线程就会抛出InterruptException之后继续往下执行,最后就执行完毕了 -
当线程池发现所有的Runnable都执行完毕后,就可以顺利关闭线程池,结束程序
class MyRunnable implements Runnable { private final String x = new String(); @Override public void run() { synchronized (x){ try { x.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("run 执行完毕"); } } public class Main { public static void main(String[] args) { ExecutorService threadPool = new ThreadPoolExecutor(5, 10, 1, TimeUnit.SECONDS, new LinkedBlockingQueue(1)); threadPool.execute(new MyRunnable()); threadPool.execute(new MyRunnable()); //使用shutdownNow()关闭线程池 List<Runnable> list = threadPool.shutdownNow(); System.out.println("shutdownNow() 线程池"); } }
shutdown() 线程池 run 执行完毕 run 执行完毕 java.lang.InterruptedException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:502) at com.mytest.thread.MyRunnable.run(Main.java:31) java.lang.InterruptedException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:502) at com.mytest.thread.MyRunnable.run(Main.java:31) //程序停止
-
-
但是因为
shutdownNow()
只能抛出一个InterruptException去解决被阻塞的线程的等待,所以假设Runnable是自愿一直运行的话,那也没办法停止此线程-
在此例中,Runnable一直在while loop中运行,所以被
shutdownNow()
的线程池即使发出了InterruptException,也没办法停止这两个线程 -
实际上,在java中也没有任何手段可以停止他们,只能透过手动强制关闭程式来解决,所以在写多线程时,要小心避免写出这种无限循环
class MyRunnable implements Runnable { @Override public void run() { int i = 0; while (i == 0) { //do nothing } System.out.println("run 执行完毕"); } } public class Main { public static void main(String[] args) { ExecutorService threadPool = new ThreadPoolExecutor(1, 10, 1, TimeUnit.SECONDS, new LinkedBlockingQueue(1)); threadPool.execute(new MyRunnable()); threadPool.execute(new MyRunnable()); //使用shutdownNow()关闭线程池 threadPool.shutdownNow(); System.out.println("shutdownNow() 线程池"); } }
shutdownNow() 线程池 //线程仍在运行...
-
-
Java多线程 - 线程池 ThreadPoolExecutor类的使用
最新推荐文章于 2021-04-26 23:04:54 发布