Java线程之线程池的使用

版权声明:未经本人允许,严禁转载 https://blog.csdn.net/lmh_19941113/article/details/85315949

 在Java中进行异步操作时,线程必不可少,但如果频繁的创建、销毁一个线程,这是很耗性能的,所以线程池就应运而生了,Java中主要有newFixedThreadPoolnewCachedThreadPoolnewSingleThreadExecuternewScheduledThreadPool这四种线程池。

  用法 具体实现
newFixedThreadPool Executors.newFixedThreadPool(int)
Executors.newFixedThreadPool(int,ThreadFactory)
new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue())
newCachedThreadPool Executors.newCachedThreadPool()
Executors.newCachedThreadPool(ThreadFactory)
new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
newSingleThreadExecuter Executors.newSingleThreadExecutor()
Executors.newSingleThreadExecutor(ThreadFactory)
new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
newScheduledThreadPool Executors.newScheduledThreadPool(int)
Executors.newScheduledThreadPool(int,ThreadFactory)
new ScheduledThreadPoolExecutor(corePoolSize);


ScheduledThreadPoolExecutor是extend与ThreadPoolExecutor的子类,传入参数如下super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());

  • newFixedThreadPool:将创建一个固定长度的线程池,每当提交一个任务时将创建一个线程。直到达到线程池的最大数量,这时线程池的规模将不再变化(如果某个线程发生了未预测的Exception而结束,那么线程池会补充一个新的线程)
  • newCachedThreadPool:将创建一个可缓存的线程池,如果线程池的规模超过了处理需求时,那么将回收空闲的线程,而当需求增加时,则可以添加新的线程,线程池的规模不存在任何限制
  • newSingleThreadExecuter:是一个单线程的Executor,它创建单个工作者来执行任务,如果这个线程异常结束,会创建另一个线程来替代,newSingleThreadExecuter确保依照任务在队列中的顺序来串行执行。
  • newScheduledThreadPool:创建一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer。

ExecutorService

 由于线程池都是实现了Executor接口,但Executor没有生命周期,所以为了解决执行服务的生命周期问题,Executor扩展了ExecutorService接口,添加了一些用于生命周期的管理方法(同时还有一些用于任务提交的便利方法)。

  • shutdown():执行平缓的关闭过程,不在接受新的任务,同时等待已经提交的任务执行完成(包括那些还未开始执行的任务)
  • shutdownNow():将尝试取消所有运行中的任务,返回所有未执行的任务
  • isTerminated():可以判断ExecutorService是否已经终止。可以通过轮询来判断
  • awaitTermination(long timeout, TimeUnit unit):等待timeout后判断是否终止

newScheduledThreadPool

 在Java中,Timer负责管理延迟任务以及周期性任务,然而Timer存在以下的一些缺陷,

  • Timer在执行所有定时任务时只会创建一个线程,如果某个任务的执行时间过长,那会将破坏其他TimerTask的定时精确性。而线程池能弥补这个缺陷,它可以提供多个线程来执行延时任务和周期任务
  • 如果TimerTask抛出一个未检查的异常,那么Timer将表现出糟糕的行为。Timer线程并不捕获异常,因此当TimerTask抛出未检查的异常时将终止定时线程。在这种情况下,Timer也不会恢复线程的执行,而是会错误的认为整个Timer都被取消了。因此,已经被调度但尚未执行的TimerTask将不会执行,新的任务也不能被调度

 因此如果要构建自己的调度服务,可以使用DelayQueue,它实现了BlockingQueue,并为newScheduledThreadPool(RxJava就是采用的newScheduledThreadPool来管理延迟任务及周期性任务的)提供调度功能。

携带结果的任务Callable与Future

 Executor框架使用Runnable作为其基本的任务表示形式。Runnable是一种有很大局限的抽象,虽然run能写入到日志文件或者将结果放入某个共享的数据结构,但它不能返回一个值或者抛出一个受检查的异常。Callable是一种更好的抽象:它认为主入口点(即call)将返回一个值,并可能抛出一个异常。
 Future表示一个任务的生命周期,并提供相应的方法来判断是否已经完成或取消,以及获取任务的结果和取消任务等。在Future规范中包含的隐含意义是,任务的生命周期只能前进,不能后退,就像ExecutorService的生命周期一样,当某个任务完成后,它就永远停留在完成状态上。Future有如下方法:

cancel(boolean mayInterruptIfRunning) isCancelled() isDone() get() get(long timeout, TimeUnit unit)
取消任务 判断是否取消 当前任务是否执行完成 拿到返回的值
如果任务已经完成则立即返回一个值或者抛异常
如果任务未完成,则将阻塞直任务完成
同get(),但加了时间限制,如果在指定时间内未拿到值则抛异常

CompletionService:Executor与BlockingQueue

 如果向Executor提交一组任务,并且希望在计算完成后获得结果,那么可以保留与每个任务关联的Future,然后反复使用get方法,同时将参数timeout指定为0,从而通过轮询来判断任务是否完成。这种方法虽然可行,但却有些繁琐。幸运的是,还有一种更好的方法:完成服务(CompletionService)
 CompletionService将Executor和BlockingQueue的功能融合在一起。你可以将Callable任务提交给它来执行,然后使用类似队列的操作take和poll等方法来获得已完成的结果,而这些结果会在完成时将被封装为Future。ExecutorCompletionService实现了CompletionService,并将计算结果委托给一个Executor。

猜你喜欢

转载自blog.csdn.net/lmh_19941113/article/details/85315949
今日推荐