Java多线程(二) 线程池

1.为什么要使用线程池

在编程中经常会使用线程来进行异步处理,但是每个线程的创建和销毁都有一定的系统资源消耗。如果每次执行一个任务都要开一个新线程去执行,则这些现成的创建和销毁将消耗大量的系统资源,所以利用线程池来来提高系统资源的利用效率,并简化线程的管理。

2.创建线程池

在Java1.5中提供了Executor框架来用于把任务的提交和执行解耦,通过execute和submit方法将Runnable或Callable任务提交给Executor框架来执行。在Executor框架中可以通过ThreadPoolExecutor来创建一个线程池,ThreadPoolExecutor的其中一个构造函数

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
       .....
    }

这些参数的作用如下:

  • corePoolSize: 核心线程数,默认情况下线程池是空的,只有任务提交的时候才会创建线程。如果当前运行的线程数小于corePoolSize,则创建新的线程来执行任务,如果运行的线程数大于corePoolSize则不再创建线程。如果调用线程池的prestartAllCoreThreads()方法,则会提前创建并启动所有的核心线程,等待处理任务。
  • maximumPoolSize:线程池允许创建的最大线程数,如果任务队列满了并且线程数小于maximumPoolSize,则线程池仍旧会创建新的线程来处理任务。
  • keepAliveTime:非核心线程闲置的超时时间。超过这个时间则回收,如果任务很多,并且每个任务的执行时间很短,则可以调大keepAliveTime来提高线程的利用率。
  • TimeUnit:keepAliveTime参数的时间单位,可以是时分秒等
  • workQueue:任务队列
  • threadFactory:线程工厂。可以使用此工厂为每个线程设置线程名称,以及线程优先级等。
  • handler:饱和策略。这是当任务队列和线程池都满了以后采取的应对策略。默认采用的是AbortPolicy,即拒接处理新任务,并抛出RejectedExecutionException异常。

线程池的饱和策略除了默认的AbortPolicy以外还包括三种策略:
(1).CallerRunsPolicy:用调用者所处的线程来处理任务。这样会影响调用者线程的性能
(2).DiscardPolicy:丢弃不能执行的任务,不做任何处理。
(3).DiscardOldestPolicy:丢弃队列最近的任务,并执行当前任务。

3.线程池执行过程

当有一个新的任务提交到线程池时,线程池的处理过程如下图所示:

在这里插入图片描述

具体的处理流程分为如下几个过程:

  • 当任务提交后,线程池首先判断当前是否有空闲线程,如果有则执行任务
  • 如果没有空闲线程,则判断当前的线程数是否达到了核心线程数,如果没有则创建新的线程来执行任务。
  • 如果已达到核心线程数则判断当前的阻塞队列是否已满,如果没满则添加到队列,等候处理。
  • 如果队列已满,判断当前的线程数是否已经达到了最大线程数,如果没有则创建非核心线程来处理任务,否则就执行饱和策略,默认抛出RejectedExecutionException异常
4.常用线程池

在用到线程池的时候,我们都是使用Executors提供的通用线程池创建方法,去创建不同配置的线程池。目前Executors框架提供了5种常用的线程池。

4.1 newCachedThreadPool

它是一种用来处理大量短时间任务的线程池。

 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
  • 它会试图缓存线程用于复用,当无缓存线程可用时,就会创建新的工作线程
  • 如果线程闲置的时间超过60秒,则终止线程并移出缓存。
  • 长时间闲置时,这种线程池不会消耗什么资源,内部使用SynchronousQueue阻塞队列来实现,它是一个不存储数据的阻塞队列
4.2 newFixedThreadPool

复用指定数目(nThreads)的线程。它的定义如下:

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

newFixedThreadPool线程池的特点:

  • 任何时间最多能有nThreads个线程是活动的。
  • 如果超出最大活动线程的数量,将在工作队列中等待空闲线程的出现。
  • 如果有工作线程退出则会有新的线程创建,以达到指定的数目nThreads,并且创建的线程不会回收。
4.3 newSingleThreadExecutor

SingleThreadExecutor是只包含一个工作线程的线程池,它的定义如下:

 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

它的核心线程数和线程总数都为1,意味着SingleThreadExecutor只有一个核心线程。当有任务提交时如果线程池还没有创建核心线程则先创建核心线程后开始处理任务,如果核心线程已经创建,则将任务放置在阻塞队列等候处理。因此SingleThreadExecutor线程池能保证所有的任务在一个线程中串行执行。

4.4 newScheduledThreadPool

ScheduledThreadPool是一种能够定时和周期性执行的线程池,它的定义如下:

  public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

ScheduledThreadPoolExecutor类继承自ThreadPoolExecutor,最终调用的还是ThreadPoolExecutor的构造函数。当调用scheduleAtFixedRate()或者scheduleWithFixedDelay()方法的时候,会将任务添加到DelayedWorkQueue队列中。DelayedWorkQueue队列会将任务进行排序,先要执行的任务放置在前边。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值