四大线程池特点介绍和适用场景

在日常应用开发中,经常会有一些耗时操作,比如数据库操作、网络请求等,碰见这种情况,我们一般会怎么做呢?当然第一反应就是放到子线程去异步处理,张手就一个new Thread().start就来了,这样的操作在线程少的情况下是没问题的,也能实现功能,但这样久之会造成严重的性能损耗,有的同学就会问为什么?我告诉你为什么,原因如下:

一、JAVA线程机制是抢占性质的,new Thread出来的匿名线程非常难以管理,都是些野猴子,缺乏管束,虽然JAVA提供了线程优先级的方法,但通常效果并不理想,有时候就很混乱,而且JAVA线程机制会给每个线程提供时间片,“野”线程多了,自然会影响耗能。

二、JAVA中你每次new Thread的时候,在难以管理的情形下,销毁线程的性能是很差的,而线程池复用线程的特性极大的提高了效率和性能。

三、new Thread功能比较单一,没有定时执行、线程中断等功能。

好了,上面三点就是原因了,所以线程池还是很有必要的,接下来我们来介绍一下吧。

在JAVA JDK1.5上提供了Executor框架,这框架就是用来把任务的提交和执行解耦,其核心成员就是ThreadPoolExecutor,这也是线程池的核心实现类。

corePoolSize

表示核心线程数,默认情况下,线程池是空的,只有提交任务的时候才会创建线程,核心线程会在线程池中一直存活,只不过是处于闲置状态,除非你把ThreadPoolExecutor的allowCoreThreadTimeOut属性设为true,就会有超时策略,即核心线程的生命周期会受keepAliveTime的限制,时间一到,就会终止。还有,如果你调用线程池的prestartAllCoreThread方法,线程池就会提前重新创建并启动所有的核心线程去等待任务

maximumPoolSize

表示允许线程池创建的最大线程数。当活动的线程小于maximumPoolSize设定的线程数,就创建线程执行任务,否则新任务被阻塞,排队等待。

keepAliveTime

表示非核心线程闲置的超时时长,超过时间则会被回收。如果遇见任务短而且很多的情况,你可以通过调大这个属性值来提高线程的利用率。

TimeUnit

表示keepAliveTime的时间单位,这是一个枚举,从天(DAYS)到纳秒(NANOSECONDS)。

workQueue

表示一个阻塞的任务队列,线程池的execute方法提交的Runnable对象会存储在这个参数中,阻塞队列的概念就不说了,大家自行百度。

ThreadFactory

表示线程工厂,线程池在创建线程的就是用的它,用Thread newThread(Runnable r)来执行创建线程操作。

RejectedExecutionHandler

表示线程池的饱和策略,用于线程池和任务队列满了的情况下使用的策略,默认是abordPolicy,表示无法处理新任务,这个时候ThreadPoolExecutor会调用handler的rejectedExecution方法通知调用者,并会直接抛出一个RejectedExecutionException,此外还有三种策略,分别是CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy,这些东西并不常用,但我想这个还是有必要普及一下,
(1)CallerRunsPolicy:用调用者所在的线程处理任务,也就是说,放下手中的活帮我处理掉的意思。
(2)DiscardPolicy:直接抛弃新任务
(3)DiscardOldestPolicy:丢弃队列中最近的任务,并执行新任务

线程池工具自带的线程池

FixedThreadPool

这类线程池的特点就是里面全是核心线程,没有非核心线程,也没有超时机制,任务大小也是没有限制的,数量固定,即使是空闲状态,线程不会被回收,除非线程池被关闭,从构造方法也可以看出来,只有两个参数,一个是指定的核心线程数,一个是线程工厂,keepAliveTime无效。任务队列采用了无界的阻塞队列LinkedBlockingQueue,执行execute方法的时候,运行的线程没有达到corePoolSize就创建核心线程执行任务,否则就阻塞在任务队列中,有空闲线程的时候去取任务执行。由于该线程池线程数固定,且不被回收,线程与线程池的生命周期同步,所以适用于任务量比较固定但耗时长的任务

CachedThreadPool

这类线程池的特点就是里面没有核心线程,全是非核心线程其maximumPoolSize设置为Integer.MAX_VALUE,线程可以无限创建,当线程池中的线程都处于活动状态的时候,线程池会创建新的线程来处理新任务,否则会用空闲的线程来处理新任务,这类线程池的空闲线程都是有超时机制的,keepAliveTime在这里是有效的,时长为60秒,超过60秒的空闲线程就会被回收,当线程池都处于闲置状态时,线程池中的线程都会因为超时而被回收,所以几乎不会占用什么系统资源。任务队列采用的是SynchronousQueue,这个队列是无法插入任务的,一有任务立即执行,所以CachedThreadPool比较适合任务量大但耗时少的任务

ScheduledThreadPool
这类线程池核心线程数量是固定的,好像和FixThreadPool有点像,但是它的非核心线程是没有限制的,并且非核心线程一闲置就会被回收,keepAliveTime同样无效,因为核心线程是不会回收的,当运行的线程数没有达到corePoolSize的时候,就新建线程去DelayedWorkQueue中取ScheduledFutureTask然后才去执行任务,否则就把任务添加到DelayedWorkQueue,DelayedWorkQueue会将任务排序,按新建一个非核心线程顺序执行,执行完线程就回收,然后循环。任务队列采用的DelayedWorkQueue是个无界的队列,延时执行队列任务。综合来说,这类线程池适用于执行定时任务和具体固定周期的重复任务

SingleThreadPool
这类线程池顾名思义就是一个只有一个核心线程的线程池,从构造方法来看,它可以单独执行,也可以与周期线程池结合用。其任务队列是LinkedBlockingQueue,这是个无界的阻塞队列,因为线程池里只有一个线程,就确保所有的任务都在同一个线程中顺序执行,这样就不需要处理线程同步的问题。这类线程池适用于多个任务顺序执行的场景。

execute()方法解析(JDK1.8)
  execute():提交任务交给线程池运行

public class ThreadPoolExecutor extends AbstractExecutorService {
    public void execute(Runnable command) {
        //任务为空则报空异常
        if (command == null)
            throw new NullPointerException();
        /*
         * 1. 如果正在运行的线程数少于corePoolSize。那么就尝试去开始一个新线程并用传入的command作为它第一个任务,      * 然后让addworker去原子性的检查线程池的运行状态和线程数量,以至于能提前知道是否能添加线程进去。      * 如果成功则线程运行,不成功就会去到下一步,并且再获取一次ctl的值(ctl是用来获取线程状态和线程数的)
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        /*
         * 2.如果一个任务能被成功的排队,那么仍然需要双重检查是否我们需要添加一个新的线程(因为有可能会第一次检查完后有一个线程销毁,所以需要双重检查)      * 或者进入此方法后线程关闭。所以很有必要重新检查一遍状态是否需要回滚排队,是否停止或开始一个新线程或是否不存在
         */
     if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
         /* 
          *3.如果我们无法将任务进行排队(即进入队列中),那么我们尝试添加一个新线程,如果失败我们就使用拒绝策略拒绝这个任务
          */
        else if (!addWorker(command, false))
            reject(command);
    }
}

线程池的一些常用方法
  submit():提交任务,能够返回执行结果execute+Future

shutdown():关闭线程池,等待任务都执行完

shutdownNow():关闭线程池,不等待任务执行完

getTaskCount():线程池已执行和未执行的任务总数

getCompletedTaskCount():已完成的任务数量

getPoolSize():线程池当前线程数量

getActiveCount():当前线程池中正在执行任务的线程数量

Executor线程池创建的四种线程
  newFixedThreadPool:创建的是定长的线程池,可以控制线程最大并发数,超出的线程会在线程中等待,使用的是无界队列,核心线程数和最大线程数一样,当线程池中的线程没有任务时候立刻销毁,使用默认线程工厂。

newSingleThreadExecutor:创建的是单线程化的线程池,只会用唯一一个工作线程执行任务,可以指定按照是否是先入先出,还是优先级来执行任务。同样使用无界队列,核心线程数和最大线程数都是1个,同样keepAliveTime为0,可选择是否使用默认线程工厂。

newCachedThreadPool:设定一个可缓存的线程池,当线程池长度超过处理的需要,可以灵活回收空闲线程,如果没有可以回收的才新建线程。没有核心线程数,当线程没有任务60s之后就会回收空闲线程,使用有界队列。同样可以选择是否使用默认线程工厂。

newScheduledThreadPool:支持线程定时操作和周期性操作。
下面是方法的源码:

public class Executors {
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue());
    }
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue()));
    }
    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue(),
                                    threadFactory));
    }
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue());
    }
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue(),
                                      threadFactory);
    }
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }
}R

Reference:https://www.jianshu.com/p/ac4c38dd77b7

你可能感兴趣的:(四大线程池特点介绍和适用场景)