Java线程池

线程池:其实就是⼀个容纳多个线程的容器,其中的线程可以反复使⽤,省去了频繁创建线程对 象的操作,⽆需反复创建线程⽽消耗过多资源。

合理利⽤线程池能够带来的好处:

  • 降低资源消耗:减少了创建和销毁线程的次数,每个⼯作线程都可以被重复利⽤,可执⾏多个任务。
  • 提⾼响应速度:当任务到达时,任务可以不需要的等到线程创建就能⽴即执⾏。
  • 提⾼线程的可管理性:可以根据系统的承受能⼒,调整线程池中⼯作线线程的数⽬,防⽌因为消耗过多的内存,⽽把服务器累趴下(每个线程需要⼤约1MB内存,线程开的越多,消耗的内存也就越⼤,最后死机)。

线程池的使用:

Java线程池的接⼝是 java.util.concurrent.Executor ,但是严格意义上讲 Executor并不是⼀个线程池,⽽只是⼀个执⾏线程的⼯具。真正线程池接⼝是java.util.concurrent.ExecutorService。在 java.util.concurrent.Executors 线程⼯⼚类⾥⾯提供了⼀些静态⼯⼚,⽣成⼀些常⽤的线程池。

Executors类中创建线程池的⽅法如下:

  • newFixedThreadPool 创建⼀个固定⻓度的线程池,当到达线程最⼤数量时,线程池的规模将不再变化。
  • newCachedThreadPool 创建⼀个可缓存的线程池,如果当前线程池的规模超出了处理需求,将回收空的线程;当需求增加时,会增加线程数量;线程池规模⽆限制。
  • newSingleThreadExecutor创建⼀个单线程的Executor,确保任务对了,串⾏执⾏
  • newScheduledThreadPool 创建⼀个固定⻓度的线程池,⽽且以延迟或者定时的⽅式来执⾏,类似Timer;

使⽤线程池中线程对象的步骤:

  1. 创建线程池对象。
  2. 创建Runnable接⼝⼦类对象。
  3. 提交Runnable接⼝⼦类对象。获取到了⼀个线程池ExecutorService 对象,定义了⼀个使⽤线程池对象的⽅法如下: public Future<?> submit(Runnable task) :获取线程池中的某⼀个线程对象,并执⾏。Future接⼝:⽤来记录线程任务执⾏完毕后产⽣的结果。线程池创建与使⽤。
  4. 关闭线程池(⼀般不做)。
class RunnableDemo implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"任务开始执行==========");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"任务结束===========");
    }
}
public class ThreadPool {
    public static void main(String[] args) {
        RunnableDemo runnable = new RunnableDemo();
        //ExecutorService executorService = Executors.newFixedThreadPool(5);
        //ExecutorService executorService = Executors.newCachedThreadPool();
        //ExecutorService executorService = Executors.newSingleThreadExecutor();
        //executorService.submit(runnable);
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
        scheduledExecutorService.schedule(runnable,10, TimeUnit.SECONDS);

    }
}

比较各个线程之间的区别前我们先看一下线程池创建的源代码:


    /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters and default thread factory and rejected execution handler.
     * It may be more convenient to use one of the {@link Executors} factory
     * methods instead of this general purpose constructor.
     * 使用给定的参数和默认的工厂以及执行处理器创建一个新的线程池,使用Executors比通用的构造方法更方便
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * 
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

corePoolSize:核心线程数,即使线程是空闲的,也要在池中保留线程的数量,除非设置了allowCoreThreadTimeOut。

maximumPoolSize:最大线程数,池中允许的最大线程数。

keepAliveTime:非核心线程空闲最长时长,当线程数大于核心数时,这是多余的空闲线程在终止前等待新任务的最长时间。

unit:keepAliveTime参数的时间单位。

workQueue:用于在执行任务之前保存任务的队列。该队列将只保存由execute方法提交的可运行任务。

(1)newFixedThreadPool:

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

源码注释:

Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue. At any point, at most nThreads threads will be active processing tasks. If additional tasks are submitted when all threads are active, they will wait in the queue until a thread is available. If any thread terminates due to a failure during execution prior to shutdown, a new one will take its place if needed to execute subsequent tasks. The threads in the pool will exist until it is explicitly shutdown.
Params:nThreads – the number of threads in the pool
Returns:the newly created thread pool
Throws:IllegalArgumentException – if nThreads <= 0

翻译注释:创建一个线程池,该线程池用固定数量的线程对共享无界队列进行操作。在任何时候,大多数nThreads线程都是活动的处理任务。如果在所有线程都是活动的情况下提交额外的任务,它们将在队列中等待,直到有一个线程可用。如果任何线程在关闭之前的执行过程中由于失败而终止,如果需要执行后续任务,将会有一个新的线程取代它。池中的线程将一直存在,直到显式关闭池。

方法解析:

  1. 创建一个固定长度(nThreads,创建时给的参数)的线程池。
  2. 有任务需要执行时,优先核心线程数执行,如果核心线程数不够用,将任务存放到无界队列(长度为:2^{31}-1中,队列满了之后再使用非核心线程执行。
  3. 所有线程都是活动的,都是核心线程。
  4. 当任务多于线程个数(大于固定长度)时任务会在无界队列中等待,直到有空闲线程出现。
  5. 如果线程在显示关闭之前因为失败而终止,会再起一个新的线程。
  6. 池子中的线程会一直存在,除非显示的关闭。

缺点:

  1. 核心线程数和最大线程数相同,会导致资源浪费和资源不足。比如:电商系统平时需求5个线程就可以搞定,为了兼容大促活动,把固定长度设置成10,在没有大促的情况下,有5个线程就是空闲的,造成资源浪费。如果设置固定长度为5,在大促活动下线程数不够用,会影响系统响应效率。
  2. 存放任务的队列是无界队列,无解队列是想存多少就给存多少,没有上限,这样可能会导致内存溢出,极少出现。

(2)newCachedThreadPool

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());

源码注释:

Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available. These pools will typically improve the performance of programs that execute many short-lived asynchronous tasks. Calls to execute will reuse previously constructed threads if available. If no existing thread is available, a new thread will be created and added to the pool. Threads that have not been used for sixty seconds are terminated and removed from the cache. Thus, a pool that remains idle for long enough will not consume any resources. Note that pools with similar properties but different details (for example, timeout parameters) may be created using ThreadPoolExecutor constructors.
Returns:the newly created thread pool

翻译注释:创建一个线程池,该线程池根据需要创建新线程,但在以前构建的线程可用时将重用它们。这些池通常会提高执行许多短期异步任务的程序的性能。如果可用,执行的调用将重用以前构造的线程。如果没有可用的线程,将创建一个新线程并将其添加到池中。60秒内未被使用的线程将被终止并从缓存中删除。因此,一个空闲时间足够长的池将不会消耗任何资源。

注意,可以使用ThreadPoolExecutor构造函数创建具有相似属性但不同细节(例如,超时参数)的池。

 方法解析:

  1. 创建一个没有核心线程的线程池,所有线程都属于非核心线程。
  2. 因为是非核心线程,如果60s没有被使用,线程终止并从缓存中删除。
  3. 有多少个任务就会创建多少个线程,最多可以创建:0x7fffffff(2^{31}-1)个线程。
  4. 任务等待队列是同步队列,就是队列中只有一个元素,进来个一就会有一个线程拿过去执行,当然在没有线程执行时第二个任务也放不进来。

缺点:

  1. 没有核心线程数以及上限过大,可能导致cup频繁的创建线程,如:两分钟过来一个任务,每个任务执行要4-5分钟,这样就会不断的重建线程,创建线程也很消耗资源。系统容易宕机。
  2. 线程数过大也容易导致内存溢出。
  3. 同步队列会导致任务没有成功的加入队列导致系统运行失败。

 (3)newSingleThreadExecutor

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

源码注释:

Creates an Executor that uses a single worker thread operating off an unbounded queue. (Note however that if this single thread terminates due to a failure during execution prior to shutdown, a new one will take its place if needed to execute subsequent tasks.) Tasks are guaranteed to execute sequentially, and no more than one task will be active at any given time. Unlike the otherwise equivalent newFixedThreadPool(1) the returned executor is guaranteed not to be reconfigurable to use additional threads. 

翻译注释:创建Executor,该Executor使用单个工作线程对无界队列进行操作。(但是请注意,如果这个线程在关闭之前的执行过程中由于失败而终止,如果需要执行后续任务,则会有一个新的线程取代它。)保证任务按顺序执行,并且在任何给定时间内活动的任务不超过一个。

与newFixedThreadPool(1)不同,返回的执行器保证不能重新配置以使用其他线程。

方法解析:

  1. 线程池只有一个线程工作,多余任务放到无界队列里面。
  2. 如果线程异常终止,会重启一个线程,保证后续任务的执行。
  3. 不管什么时间,有且仅有一个任务是活动的。
  4. 与newFixedThreadPool(1)不同,返回的执行器保证不能重新配置以使用其他线程。这句话大概意思就是必须保证有且仅有一个线程,newFixedThreadPool可以通过该其他配置可以使用其他线程。(如果有更好的理解欢迎在评论区交流)

缺点:

  1. 单线程运行,效率慢。
  2. 无界队列有可能会内存溢出,极少出现。

(4)newSingleThreadScheduledExecutor

// Executors类方法
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
    return new DelegatedScheduledExecutorService
        (new ScheduledThreadPoolExecutor(1));
}
// ScheduledThreadPoolExecutor类方法
// 继承了ThreadPoolExecutor类 super()即为new ThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

newSingleThreadScheduledExecutor源码注释:

Creates a single-threaded executor that can schedule commands to run after a given delay, or to execute periodically. (Note however that if this single thread terminates due to a failure during execution prior to shutdown, a new one will take its place if needed to execute subsequent tasks.) Tasks are guaranteed to execute sequentially, and no more than one task will be active at any given time. Unlike the otherwise equivalent newScheduledThreadPool(1) the returned executor is guaranteed not to be reconfigurable to use additional threads.

newSingleThreadScheduledExecutor翻译注释:创建单线程执行器,该执行器可以安排命令在给定延迟后运行或定期执行。(但是请注意,如果这个线程在关闭之前的执行过程中由于失败而终止,如果需要执行后续任务,则会有一个新的线程取代它。)保证任务按顺序执行,并且在任何给定时间内活动的任务不超过一个。与其他等价的newScheduledThreadPool(1)不同,返回的执行器保证不能重新配置以使用额外的线程。

ScheduledThreadPoolExecutor源码注释:

Creates a new ScheduledThreadPoolExecutor with the given core pool size.

ScheduledThreadPoolExecutor翻译注释:

使用给定的核心池大小创建一个新的ScheduledThreadPoolExecutor。

方法解析:

  1. 创建一个长度为1的单线程执行器。
  2. 队列使用延时队列,保证任务有序的、定期执行。
  3. 如果线程异常中断会重新启动一个线程保证后续任务继续执行。
  4. 任何时间池子内的线程数有且仅有一个。

定时任务线程池执行三种执行策略:

  • schedule:延迟多长时间之后只执行一次;
  • scheduledAtFixedRate固定:延迟指定时间后执行一次,之后按照固定的时长周期执行;
  • scheduledWithFixedDelay非固定:延迟指定时间后执行一次,之后按照:上一次任务执行时长 + 周期的时长 的时间去周期执行;

综合以上线程池,Executors类给出的创建线程池的模式都有一定的弊端 ,阿里的开发手册上面明确规定不建议使用Executors的线程池创建方式。结合业务场景,可以使用new ThreadPoolExecutor()方法自定义创建线程池。

public static void main(String[] args) {
    RunnableDemo runnable = new RunnableDemo();
    ThreadPoolExecutor definePool = new ThreadPoolExecutor(10, 20, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
    for (int i = 0; i < 30; i++) {
        definePool.submit(runnable);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Seventeen117

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值