深入学习Java线程池 Posted 2022-09-05 7788it
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入学习Java线程池相关的知识,希望对你有一定的参考价值。
在前面的例子中,我们都是通过new Thread
来创建一个线程,由于线程的创建和销毁都需要消耗一定的CPU资源,所以在高并发下这种创建线程的方式将严重影响代码执行效率。而线程池的作用就是让一个线程执行结束后不马上销毁,继续执行新的任务,这样就节省了不断创建线程和销毁线程的开销。
ThreadPoolExecutor
创建Java线程池最为核心的类为ThreadPoolExecutor
:
它提供了四种构造函数来创建线程池,其中最为核心的构造函数如下所示:
1234567
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
这7个参数的含义如下:
corePoolSize 线程池核心线程数。即线程池中保留的线程个数,即使这些线程是空闲的,也不会被销毁,除非通过ThreadPoolExecutor的allowCoreThreadTimeOut(true)
方法开启了核心线程的超时策略;
maximumPoolSize 线程池中允许的最大线程个数;
keepAliveTime 用于设置那些超出核心线程数量的线程的最大等待时间,超过这个时间还没有新任务的话,超出的线程将被销毁;
unit 超时时间单位;
workQueue 线程队列。用于保存通过execute方法提交的,等待被执行的任务;
threadFactory 线程创建工程,即指定怎样创建线程;
handler 拒绝策略。即指定当线程提交的数量超出了maximumPoolSize后,该使用什么策略处理超出的线程。
在通过这个构造方法创建线程池的时候,这几个参数必须满足以下条件,否则将抛出IllegalArgumentException
异常:
corePoolSize不能小于0;
keepAliveTime不能小于0;
maximumPoolSize 不能小于等于0;
maximumPoolSize不能小于corePoolSize;
此外,workQueue、threadFactory和handler不能为null,否则将抛出空指针异常。
下面举些例子来深入理解这几个参数的含义。
使用上面的构造方法创建一个线程池:
123456789101112131415161718192021
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 1, 2, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), (ThreadFactory) Thread::new, new ThreadPoolExecutor.AbortPolicy());System.out.println("线程池创建完毕");int activeCount = -1;int queueSize = -1;while (true) if (activeCount != threadPoolExecutor.getActiveCount() || queueSize != threadPoolExecutor.getQueue().size()) System.out.println("活跃线程个数 " + threadPoolExecutor.getActiveCount()); System.out.println("核心线程个数 " + threadPoolExecutor.getCorePoolSize()); System.out.println("队列线程个数 " + threadPoolExecutor.getQueue().size()); System.out.println("最大线程数 " + threadPoolExecutor.getMaximumPoolSize()); System.out.println("------------------------------------"); activeCount = threadPoolExecutor.getActiveCount(); queueSize = threadPoolExecutor.getQueue().size();
上面的代码创建了一个核心线程数量为1,允许最大线程数量为2,最大活跃时间为10秒,线程队列长度为1的线程池。
假如我们通过execute方法向线程池提交1个任务,看看结果如何:
1234567891011121314151617181920212223
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 1, 2, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), (ThreadFactory) Thread::new, new ThreadPoolExecutor.AbortPolicy());System.out.println("线程池创建完毕");threadPoolExecutor.execute(() -> sleep(100));int activeCount = -1;int queueSize = -1;while (true) if (activeCount != threadPoolExecutor.getActiveCount() || queueSize != threadPoolExecutor.getQueue().size()) System.out.println("活跃线程个数 " + threadPoolExecutor.getActiveCount()); System.out.println("核心线程个数 " + threadPoolExecutor.getCorePoolSize()); System.out.println("队列线程个数 " + threadPoolExecutor.getQueue().size()); System.out.println("最大线程数 " + threadPoolExecutor.getMaximumPoolSize()); System.out.println("------------------------------------"); activeCount = threadPoolExecutor.getActiveCount(); queueSize = threadPoolExecutor.getQueue().size();
ThreadPoolExecutor的execute和submit方法都可以向线程池提交任务,区别是,submit方法能够返回执行结果,返回值类型为Future
sleep方法代码:
12345678
private static void sleep(long value) try System.out.println(Thread.currentThread().getName() + "线程执行sleep方法"); TimeUnit.SECONDS.sleep(value); catch (InterruptedException e) e.printStackTrace();
启动程序,控制台输出如下:
线程池核心线程数量为1,通过execute提交了一个任务后,由于核心线程是空闲的,所以任务被执行了。由于这个任务的逻辑是休眠100秒,所以在这100秒内,线程池的活跃线程数量为1。此外,因为提交的任务被核心线程执行了,所以并没有线程需要被放到线程队列里等待,线程队列长度为0。
假如我们通过execute方法向线程池提交2个任务,看看结果如何:
12
threadPoolExecutor.execute(() -> sleep(100));threadPoolExecutor.execute(() -> sleep(100));
线程池核心线程数量为1,通过execute提交了2个任务后,一开始核心线程是空闲的,Thread-0被执行。由于这个任务的逻辑是休眠100秒,所以在这100秒内,线程池的活跃线程数量为1。因为核心线程数量为1,所以另外一个任务在这100秒内不能被执行,于是被放到线程队列里等待,线程队列长度为1。
假如我们通过execute方法向线程池提交3个任务,看看结果如何:
123
threadPoolExecutor.execute(() -> sleep(100));threadPoolExecutor.execute(() -> sleep(100));threadPoolExecutor.execute(() -> sleep(100));
这三个任务都是休眠100秒,所以核心线程池中第一个任务正在被执行,第二个任务被放入到了线程队列。而当第三个任务被提交进来时,线程队列满了(我们定义的长度为1),由于该线程池允许的最大线程数量为2,所以线程池还可以再创建一个线程来执行另外一个任务,于是乎之前在线程队列里的线程被取出执行(FIFO),第三个任务被放入到了线程队列。
改变第二个和第三个任务的睡眠时间,观察输出:
123
threadPoolExecutor.execute(() -> sleep(100));threadPoolExecutor.execute(() -> sleep(5));threadPoolExecutor.execute(() -> sleep(5));
第二个任务提交5秒后,任务执行完毕,所以线程队列里的任务被执行,于是队列线程个数为0,活跃线程数量为2(第一个和第三个任务)。再过5秒后,第三个任务执行完毕,于是活跃线程数量为1(第一个100秒还没执行完毕)。
在第三个任务结束的瞬间,我们观察线程快照:
可以看到,线程池中有两个线程,Thread-0在执行第一个任务(休眠100秒,还没结束),Thread-1执行完第三个任务后并没有马上被销毁。过段时间后(10秒钟后)再观察线程快照:
可以看到,Thread-1这个线程被销毁了,因为我们在创建线程池的时候,指定keepAliveTime 为10秒,10秒后,超出核心线程池线程外的那些线程将被销毁。
假如一次性提交4个任务,看看会怎样:
1234
threadPoolExecutor.execute(() -> sleep(100));threadPoolExecutor.execute(() -> sleep(100));threadPoolExecutor.execute(() -> sleep(100));threadPoolExecutor.execute(() -> sleep(100));
因为我们设置的拒绝策略为AbortPolicy,所以最后提交的那个任务直接被拒绝了。更多拒绝策略下面会介绍到。
关闭线程池
线程池包含以下几个状态:
当线程池中所有任务都处理完毕后,线程并不会自己关闭。我们可以通过调用shutdown
和shutdownNow
方法来关闭线程池。两者的区别在于:
shutdown
方法将线程池置为shutdown状态,拒绝新的任务提交,但线程池并不会马上关闭,而是等待所有正在折行的和线程队列里的任务都执行完毕后,线程池才会被关闭。所以这个方法是平滑的关闭线程池。
shutdownNow
方法将线程池置为stop状态,拒绝新的任务提交,中断正在执行的那些任务,并且清除线程队列里的任务并返回。所以这个方法是比较“暴力”的。
举两个例子观察下两者的区别:
shutdown
例子:
123456789101112131415161718192021222324252627282930313233343536373839
public static void main(String[] args) ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 2, 4, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(2), (ThreadFactory) Thread::new, new ThreadPoolExecutor.AbortPolicy()); threadPoolExecutor.execute(new shortTask()); threadPoolExecutor.execute(new longTask()); threadPoolExecutor.execute(new longTask()); threadPoolExecutor.execute(new shortTask()); threadPoolExecutor.shutdown(); System.out.println("已经执行了线程池shutdown方法");static class shortTask implements Runnable @Override public void run() try TimeUnit.SECONDS.sleep(1); System.out.println(Thread.currentThread().getName() + "执行shortTask完毕"); catch (InterruptedException e) System.err.println("shortTask执行过程中被打断" + e.getMessage()); static class longTask implements Runnable @Override public void run() try TimeUnit.SECONDS.sleep(5); System.out.println(Thread.currentThread().getName() + "执行longTask完毕"); catch (InterruptedException e) System.err.println("longTask执行过程中被打断" + e.getMessage());
启动程序,控制台输出如下:
可以看到,虽然在任务都被提交后马上执行了shutdown
方法,但是并不会马上关闭线程池,而是等待所有被提交的任务都执行完了才关闭。
shutdownNow
例子:
1234567891011121314151617181920212223242526272829303132333435363738394041
public static void main(String[] args) ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 2, 4, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(2), (ThreadFactory) Thread::new, new ThreadPoolExecutor.AbortPolicy()); threadPoolExecutor.execute(new shortTask()); threadPoolExecutor.execute(new longTask()); threadPoolExecutor.execute(new longTask()); threadPoolExecutor.execute(new shortTask()); List<Runnable> runnables = threadPoolExecutor.shutdownNow();
启动程序,控制台输出如下:
可以看到,在执行shutdownNow
方法后,线程池马上就被关闭了,正在执行中的两个任务被打断,并且返回了线程队列中等待被执行的两个任务。
通过上面两个例子我们还可以看到shutdown
和shutdownNow
方法都不是阻塞的。常与shutdown
搭配的方法有awaitTermination
。
awaitTermination
方法接收timeout和TimeUnit两个参数,用于设定超时时间及单位。当等待超过设定时间时,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。该方法是阻塞的:
123456789101112131415161718192021222324252627282930313233343536373839404142434445
public static void main(String[] args) throws InterruptedException ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 2, 4, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(2), (ThreadFactory) Thread::new, new ThreadPoolExecutor.AbortPolicy()); threadPoolExecutor.execute(new shortTask()); threadPoolExecutor.execute(new longTask()); threadPoolExecutor.execute(new longTask()); threadPoolExecutor.execute(new shortTask()); threadPoolExecutor.shutdown(); boolean isShutdown = threadPoolExecutor.awaitTermination(3, TimeUnit.SECONDS); if (isShutdown) System.out.println("线程池在3秒内成功关闭"); else System.out.println("等了3秒还没关闭,不等了╰(‵□′)╯"); System.out.println("------------");static class shortTask implements Runnable @Override public void run() try TimeUnit.SECONDS.sleep(1); System.out.println(Thread.currentThread().getName() + "执行shortTask完毕"); catch (InterruptedException e) System.err.println("shortTask执行过程中被打断" + e.getMessage()); static class longTask implements Runnable @Override public void run() try TimeUnit.SECONDS.sleep(5); System.out.println(Thread.currentThread().getName() + "执行longTask完毕"); catch (InterruptedException e) System.err.println("longTask执行过程中被打断" + e.getMessage());
启动程序输出如下:
4大拒绝策略
当线程池无法再接收新的任务的时候,可采取如下四种策略:
CallerRunsPolicy
CallerRunsPolicy
策略:由调用线程处理该任务:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
public static void main(String[] args) throws InterruptedException ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 2, 3, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), (ThreadFactory) Thread::new, new ThreadPoolExecutor.CallerRunsPolicy()); threadPoolExecutor.execute(new shortTask("任务1")); threadPoolExecutor.execute(new longTask("任务2")); threadPoolExecutor.execute(new longTask("任务3")); threadPoolExecutor.execute(new shortTask("任务4")); threadPoolExecutor.execute(new shortTask("任务5")); threadPoolExecutor.shutdown();static class shortTask implements Runnable private String name; public shortTask(String name) this.name = name; @Override public void run() try TimeUnit.SECONDS.sleep(1); System.out.println(Thread.currentThread().getName() + "执行shortTask-name-" + name + "完毕"); catch (InterruptedException e) System.err.println("shortTask执行过程中被打断" + e.getMessage()); static class longTask implements Runnable private String name; public longTask(String name) this.name = name; @Override public void run() try TimeUnit.SECONDS.sleep(5); System.out.println(Thread.currentThread().getName() + "执行longTask-name-" + name + "完毕"); catch (InterruptedException e) System.err.println("longTask执行过程中被打断" + e.getMessage());
上面的线程池最多只能一次性提交4个任务,第5个任务提交后会被拒绝策略处理。启动程序输出如下:
可以看到,第5个提交的任务由调用线程(即main线程)处理该任务。
AbortPolicy
AbortPolicy
策略:丢弃任务,并抛出RejectedExecutionException
异常。前面的例子就是使用该策略,所以不再演示。
DiscardOldestPolicy
DiscardOldestPolicy
策略:丢弃最早被放入到线程队列的任务,将新提交的任务放入到线程队列末端:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
public static void main(String[] args) throws InterruptedException ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 2, 3, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), (ThreadFactory) Thread::new, new ThreadPoolExecutor.DiscardOldestPolicy()); threadPoolExecutor.execute(new shortTask("任务1")); threadPoolExecutor.execute(new longTask("任务2")); threadPoolExecutor.execute(new longTask("任务3")); threadPoolExecutor.execute(new shortTask("任务4")); threadPoolExecutor.execute(new shortTask("任务5")); threadPoolExecutor.shutdown();static class shortTask implements Runnable private String name; public shortTask(String name) this.name = name; @Override public void run() try TimeUnit.SECONDS.sleep(1); System.out.println(Thread.currentThread().getName() + "执行shortTask-name-" + name + "完毕"); catch (InterruptedException e) System.err.println("shortTask执行过程中被打断" + e.getMessage()); static class longTask implements Runnable private String name; public longTask(String name) this.name = name; @Override public void run() try TimeUnit.SECONDS.sleep(5); System.out.println(Thread.currentThread().getName() + "执行longTask-name-" + name + "完毕"); catch (InterruptedException e) System.err.println("longTask执行过程中被打断" + e.getMessage());
启动程序输出如下:
可以看到最后提交的任务被执行了,而第3个任务是第一个被放到线程队列的任务,被丢弃了。
DiscardPolicy
DiscardPolicy
策略:直接丢弃新的任务,不抛异常:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
public static void main(String[] args) throws InterruptedException ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 2, 3, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), (ThreadFactory) Thread::new, new ThreadPoolExecutor.DiscardPolicy()); threadPoolExecutor.execute(new shortTask("任务1")); threadPoolExecutor.execute(new longTask("任务2")); threadPoolExecutor.execute(new longTask("任务3")); threadPoolExecutor.execute(new shortTask("任务4")); threadPoolExecutor.execute(new shortTask("任务5")); threadPoolExecutor.shutdown();static class shortTask implements Runnable private String name; public shortTask(String name) this.name = name; @Override public void run() try TimeUnit.SECONDS.sleep(1); System.out.println(Thread.currentThread().getName() + "执行shortTask-name-" + name + "完毕"); catch (InterruptedException e) System.err.println("shortTask执行过程中被打断" + e.getMessage()); static class longTask implements Runnable private String name; public longTask(String name) this.name = name; @Override public void run() try TimeUnit.SECONDS.sleep(5); System.out.println(Thread.currentThread().getName() + "执行longTask-name-" + name + "完毕"); catch (InterruptedException e) System.err.println("longTask执行过程中被打断" + e.getMessage());
启动程序,输出如下:
第5个任务直接被拒绝丢弃了,而没有抛出任何异常。
线程池工厂方法
除了使用ThreadPoolExecutor的构造方法创建线程池外,我们也可以使用Executors
提供的工厂方法来创建不同类型的线程池:
newFixedThreadPool
查看newFixedThreadPool
方法源码:
12345
public static ExecutorService newFixedThreadPool(int nThreads) return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
可以看到,通过newFixedThreadPool
创建的是一个固定大小的线程池,大小由nThreads
参数指定,它具有如下几个特点:
因为corePoolSize和maximumPoolSize的值都为nThreads,所以线程池中线程数量永远等于nThreads,不可能新建除了核心线程数的线程来处理任务,即keepAliveTime实际上在这里是无效的。
LinkedBlockingQueue是一个无界队列(最大长度为Integer.MAX_VALUE),所以这个线程池理论是可以无限的接收新的任务,这就是为什么上面没有指定拒绝策略的原因。
newCachedThreadPool
查看newCachedThreadPool
方法源码:
12345
public static ExecutorService newCachedThreadPool() return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
这是一个理论上无限大小的线程池:
核心线程数为0,SynchronousQueue队列是没有长度的队列,所以当有新的任务提交,如果有空闲的还未超时的(最大空闲时间60秒)线程则执行该任务,否则新增一个线程来处理该任务。
因为线程数量没有限制,理论上可以接收无限个新任务,所以这里也没有指定拒绝策略。
newSingleThreadExecutor
查看newSingleThreadExecutor
源码:
123456
public static ExecutorService newSingleThreadExecutor() return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
核心线程数和最大线程数都为1,每次只能有一个线程处理任务。
LinkedBlockingQueue队列可以接收无限个新任务。
newScheduledThreadPool
查看newScheduledThreadPool
源码:
123456789
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) return new ScheduledThreadPoolExecutor(corePoolSize);......public ScheduledThreadPoolExecutor(int corePoolSize) super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
所以newScheduledThreadPool
理论是也是可以接收无限个任务,DelayedWorkQueue也是一个无界队列。
使用newScheduledThreadPool创建的线程池除了可以处理普通的Runnable任务外,它还具有调度的功能:
1.延迟指定时间后执行:
123
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
2.按指定的速率执行:
12345
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
3.按指定的时延执行:
1234
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);executorService.scheduleWithFixedDelay( () -> System.out.println(LocalTime.now()), 1, 5, TimeUnit.SECONDS);
乍一看,scheduleAtFixedRate和scheduleWithFixedDelay没啥区别,实际它们还是有区别的:
对于这些线程池工厂方法的使用,阿里巴巴编程规程指出:
因为这几个线程池理论是都可以接收无限个任务,所以这就有内存溢出的风险。实际上只要我们掌握了ThreadPoolExecutor构造函数7个参数的含义,我们就可以根据不同的业务来创建出符合需求的线程池。一般线程池的创建可以参考如下规则:
一些API的用法
ThreadPoolExecutor提供了几个判断线程池状态的方法:
12345678910111213141516171819202122
public static void main(String[] args) throws InterruptedException ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 1, 2, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), (ThreadFactory) Thread::new, new ThreadPoolExecutor.AbortPolicy() ); threadPoolExecutor.execute(() -> try TimeUnit.SECONDS.sleep(5); catch (InterruptedException e) e.printStackTrace(); ); threadPoolExecutor.shutdown(); System.out.println("线程池为shutdown状态:" + threadPoolExecutor.isShutdown()); System.out.println("线程池正在关闭:" + threadPoolExecutor.isTerminating()); System.out.println("线程池已经关闭:" + threadPoolExecutor.isTerminated()); threadPoolExecutor.awaitTermination(6, TimeUnit.SECONDS); System.out.println("线程池已经关闭" + threadPoolExecutor.isTerminated());
程序输出如下:
前面我们提到,线程池核心线程即使是空闲状态也不会被销毁,除非使用allowCoreThreadTimeOut
设置了允许核心线程超时:
12345678910111213141516
public static void main(String[] args) throws InterruptedException ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 1, 2, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), (ThreadFactory) Thread::new, new ThreadPoolExecutor.AbortPolicy() ); threadPoolExecutor.allowCoreThreadTimeOut(true); threadPoolExecutor.execute(() -> try TimeUnit.SECONDS.sleep(5); System.out.println("任务执行完毕"); catch (InterruptedException e) e.printStackTrace(); );
程序输出如下所示:
5秒后任务执行完毕,核心线程处于空闲的状态。因为通过allowCoreThreadTimeOut
方法设置了允许核心线程超时,所以3秒后(keepAliveTime设置为3秒),核心线程被销毁。核心线程被销毁后,线程池也就没有作用了,于是就自动关闭了。
值得注意的是,如果一个线程池调用了allowCoreThreadTimeOut(true)
方法,那么它的keepAliveTime
不能为0。
ThreadPoolExecutor提供了一remove
方法,查看其源码:
12345
public boolean remove(Runnable task) boolean removed = workQueue.remove(task); tryTerminate();
可看到,它删除的是线程队列中的任务,而非正在被执行的任务。举个例子:
123456789101112131415161718192021
public static void main(String[] args) throws InterruptedException ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 1, 2, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), (ThreadFactory) Thread::new, new ThreadPoolExecutor.AbortPolicy() ); threadPoolExecutor.execute(() -> try TimeUnit.SECONDS.sleep(5); System.out.println("任务执行完毕"); catch (InterruptedException e) e.printStackTrace(); ); Runnable r = () -> System.out.println("看看我是否会被删除"); threadPoolExecutor.execute(r); threadPoolExecutor.remove(r); threadPoolExecutor.shutdown();
执行程序,输出如下:
可看到任务并没有被执行,已经被删除,因为唯一一个核心线程已经在执行任务了,所以后提交的这个任务被放到了线程队列里,然后通过remove方法删除。
默认情况下,只有当往线程池里提交了任务后,线程池才会启动核心线程处理任务。我们可以通过调用prestartCoreThread
方法,让核心线程即使没有任务提交,也处于等待执行任务的活跃状态:
1234567891011121314
public static void main(String[] args) throws InterruptedException ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 2, 2, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), (ThreadFactory) Thread::new, new ThreadPoolExecutor.AbortPolicy() ); System.out.println("活跃线程数: " + threadPoolExecutor.getActiveCount()); threadPoolExecutor.prestartCoreThread(); System.out.println("活跃线程数: " + threadPoolExecutor.getActiveCount()); threadPoolExecutor.prestartCoreThread(); System.out.println("活跃线程数: " + threadPoolExecutor.getActiveCount()); threadPoolExecutor.prestartCoreThread(); System.out.println("活跃线程数: " + threadPoolExecutor.getActiveCount());
程序输出如下所示:
该方法返回boolean类型值,如果所以核心线程都启动了,返回false,反之返回true。
还有一个和它类似的prestartAllCoreThreads
方法,它的作用是一次性启动所有核心线程,让其处于活跃地等待执行任务的状态。
ThreadPoolExecutor的invokeAny方法用于随机执行任务集合中的某个任务,并返回执行结果,该方法是同步方法:
123456789101112131415161718
public static void main(String[] args) throws InterruptedException, ExecutionException ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 2, 5, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), (ThreadFactory) Thread::new, new ThreadPoolExecutor.AbortPolicy() );
启动程序,输出如下:
ThreadPoolExecutor的invokeAll则是执行任务集合中的所有任务,返回Future集合:
1234567891011121314151617181920212223
public static void main(String[] args) throws InterruptedException, ExecutionException ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 2, 5, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), (ThreadFactory) Thread::new, new ThreadPoolExecutor.AbortPolicy() ); List<Callable<Integer>> tasks = IntStream.range(0, 4).boxed().map(i -> (Callable<Integer>) () -> TimeUnit.SECONDS.sleep(ThreadLocalRandom.current().nextInt(5)); return i; ).collect(Collectors.toList()); List<Future<Integer>> futureList = threadPoolExecutor.invokeAll(tasks); futureList.stream().map(f-> try return f.get(); catch (InterruptedException | ExecutionException e) return null; ).forEach(System.out::println); threadPoolExecutor.shutdownNow();
输出如下:
总结下这些方法:
方法 描述
allowCoreThreadTimeOut(boolean value)
是否允许核心线程空闲后超时,是的话超时后核心线程将销毁,线程池自动关闭
awaitTermination(long timeout, TimeUnit unit)
阻塞当前线程,等待线程池关闭,timeout用于指定等待时间。
execute(Runnable command)
向线程池提交任务,没有返回值
submit(Runnable task)
向线程池提交任务,返回Future
isShutdown()
判断线程池是否为shutdown状态
isTerminating()
判断线程池是否正在关闭
isTerminated()
判断线程池是否已经关闭
remove(Runnable task)
移除线程队列中的指定任务
prestartCoreThread()
提前让一个核心线程处于活跃状态,等待执行任务
prestartAllCoreThreads()
提前让所有核心线程处于活跃状态,等待执行任务
getActiveCount()
获取线程池活跃线程数
getCorePoolSize()
获取线程池核心线程数
threadPoolExecutor.getQueue()
获取线程池线程队列
getMaximumPoolSize()
获取线程池最大线程数
shutdown()
让线程池处于shutdown状态,不再接收任务,等待所有正在运行中的任务结束后,关闭线程池。
shutdownNow()
让线程池处于stop状态,不再接受任务,尝试打断正在运行中的任务,并关闭线程池,返回线程队列中的任务。
以上是关于深入学习Java线程池的主要内容,如果未能解决你的问题,请参考以下文章
深入学习Java线程池
深入学习Java线程池
Java多线程高并发学习笔记——深入理解线程池
Java 并发编程深入学习——线程池及其实现原理
Java学习---深入理解线程池
死磕 java线程系列之线程池深入解析——未来任务执行流程