【并发编程】【JDK源码】J.U.C--线程池

原文:慕课网实战·高并发探索(十四):线程池 Executorjava

new Thread的弊端

每次new Thread 新建对象,性能差。
线程缺少统一管理,可能无限制的新建线程,相互竞争,可能占用过多的系统资源致使死机或者OOM(out of memory 内存溢出),这种问题的缘由不是由于单纯的new一个Thread,而是可能由于程序的bug或者设计上的缺陷致使不断new Thread形成的。
缺乏更多功能,如更多执行、按期执行、线程中断。数组

线程池的好处

重用存在的线程,减小对象建立、消亡的开销,性能好。
可有效控制最大并发线程数,提升系统资源利用率,同时能够避免过多资源竞争,避免阻塞。
提供定时执行、按期执行、单线程、并发数控制等功能。缓存

线程池类图

在线程池的类图中,咱们最常使用的是最下边的Executors,用它来建立线程池使用线程。那么在上边的类图中,包含了一个Executor框架,它是一个根据一组执行策略的调用调度执行和控制异步任务的框架,目的是提供一种将任务提交与任务如何运行分离开的机制。它包含了三个executor接口:并发

Executor:运行新任务的简单接口
ExecutorService:扩展了Executor,添加了用来管理执行器生命周期和任务生命周期的方法
ScheduleExcutorService:扩展了ExecutorService,支持Future和按期执行任务。框架

线程池核心类-ThreadPoolExecutor

参数说明:异步

ThreadPoolExecutor一共有七个参数,这七个参数配合起来,构成了线程池强大的功能。ide

corePoolSize:核心线程数量
maximumPoolSize:线程最大线程数
workQueue:阻塞队列,存储等待执行的任务,很重要,会对线程池运行过程产生重大影响高并发

当咱们提交一个新的任务到线程池,线程池会根据当前池中正在运行的线程数量来决定该任务的处理方式。处理方式有三种:
一、直接切换(SynchronusQueue)
二、无界队列(LinkedBlockingQueue)可以建立的最大线程数为corePoolSize,这时maximumPoolSize就不会起做用了。当线程池中全部的核心线程都是运行状态的时候,新的任务提交就会放入等待队列中。
三、有界队列(ArrayBlockingQueue)最大maximumPoolSize,可以下降资源消耗,可是这种方式使得线程池对线程调度变的更困难。由于线程池与队列容量都是有限的。因此想让线程池的吞吐率和处理任务达到一个合理的范围,又想使咱们的线程调度相对简单,而且还尽量下降资源的消耗,咱们就须要合理的限制这两个数量
分配技巧: [若是想下降资源的消耗包括下降cpu使用率、操做系统资源的消耗、上下文切换的开销等等,能够设置一个较大的队列容量和较小的线程池容量,这样会下降线程池的吞吐量。若是咱们提交的任务常常发生阻塞,咱们能够调整maximumPoolSize。若是咱们的队列容量较小,咱们须要把线程池大小设置的大一些,这样cpu的使用率相对来讲会高一些。可是若是线程池的容量设置的过大,提升任务的数量过多的时候,并发量会增长,那么线程之间的调度就是一个须要考虑的问题。这样反而可能会下降处理任务的吞吐量。性能

keepAliveTime:线程没有任务执行时最多保持多久时间终止(当线程中的线程数量大于corePoolSize的时候,若是这时没有新的任务提交核心线程外的线程不会当即销毁,而是等待,直到超过keepAliveTime)
unit:keepAliveTime的时间单位
threadFactory:线程工厂,用来建立线程,有一个默认的工场来建立线程,这样新建立出来的线程有相同的优先级,是非守护线程、设置好了名称
rejectHandler:当拒绝处理任务时(阻塞队列满)的策略(AbortPolicy默认策略直接抛出异常、CallerRunsPolicy用调用者所在的线程执行任务、DiscardOldestPolicy丢弃队列中最靠前的任务并执行当前任务、DiscardPolicy直接丢弃当前任务)
操作系统

corePoolSize、maximumPoolSize、workQueue 三者关系:若是运行的线程数小于corePoolSize的时候,直接建立新线程来处理任务。即便线程池中的其余线程是空闲的。若是运行中的线程数大于corePoolSize且小于maximumPoolSize时,那么只有当workQueue满的时候才建立新的线程去处理任务。若是corePoolSize与maximumPoolSize是相同的,那么建立的线程池大小是固定的。这时有新任务提交,当workQueue未满时,就把请求放入workQueue中。等待空线程从workQueue取出任务。若是workQueue此时也满了,那么就使用另外的拒绝策略参数去执行拒绝策略。

初始化方法:由七个参数组合成四个初始化方法

其余方法:

序号 方法名 描述
1 execute() 提交任务,交给线程池执行
2 submit() 提交任务,可以返回执行结果 execute+Future
3 shutdown() 关闭线程池,等待任务都执行完
4 shutdownNow() 关闭线程池,不等待任务执行完
5 getTaskCount() 线程池已执行和未执行的任务总数
6 getCompleteTaskCount() 已完成的任务数量
7 getPoolSize() 线程池当前的线程数量
8 getActiveCount() 当前线程池中正在执行任务的线程数量

线程池生命周期:

running:能接受新提交的任务,也能处理阻塞队列中的任务
shutdown:不能处理新的任务,可是能继续处理阻塞队列中任务
stop:不能接收新的任务,也不处理队列中的任务
tidying:若是全部的任务都已经终止了,这时有效线程数为0
terminated:最终状态

使用Executor建立线程池

使用Executor能够建立四种线程池:分别对应上边提到的四种线程池初始化方法。

Executors.newCachedThreadPool
Executors.newFixedThreadPool
Executors.newSingleThreadExecutor
Executors.newScheduledThreadPool

一、Executors.newCachedThreadPool

建立一个可缓存的线程池,若是线程池的长度超过了处理的须要,能够灵活回收空闲线程。若是没有可回收的就新建线程。

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

//使用方法:
public static void main(String[] args) {
    ExecutorService executorService = Executors.newCachedThreadPool();
    for (int i = 0; i < 10; i++) {
        final int index = i;
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                log.info("task:{}", index);
            }
        });
    }
    executorService.shutdown();
}

值得注意的一点是,newCachedThreadPool的返回值是ExecutorService类型,该类型只包含基础的线程池方法,但却不包含线程监控相关方法,所以在使用返回值为ExecutorService的线程池类型建立新线程时要考虑到具体状况。

二、Executors.newFixedThreadPool

定长线程池,能够线程现成的最大并发数,超出在队列等待

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

//使用方法:
public static void main(String[] args) {
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    for (int i = 0; i < 10; i++) {
        final int index = i;
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                log.info("task:{}", index);
            }
        });
    }
    executorService.shutdown();
}

三、Executors.newSingleThreadExecutor

单线程化的线程池,用惟一的一个共用线程执行任务,保证全部任务按指定顺序执行(FIFO、优先级…)。

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

//使用方法:
public static void main(String[] args) {
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 10; i++) {
        final int index = i;
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                log.info("task:{}", index);
            }
        });
    }
    executorService.shutdown();
}

四、Executors.newScheduledThreadPool

定长线程池,支持定时和周期任务执行。

//源码:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,//此处super指的是ThreadPoolExecutor
          new DelayedWorkQueue());
}

//基础使用方法:
public static void main(String[] args) {
    ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
    executorService.schedule(new Runnable() {
        @Override
        public void run() {
            log.warn("schedule run");
        }
    }, 3, TimeUnit.SECONDS);//延迟3秒执行
    executorService.shutdown();
}

ScheduledExecutorService提供了三种方法可使用:

scheduleAtFixedRate:以指定的速率执行任务
scheduleWithFixedDelay:以指定的延迟执行任务

举例:

executorService.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        log.warn("schedule run");
    }
}, 1, 3, TimeUnit.SECONDS);//延迟一秒后每隔3秒执行

小扩展:延迟执行任务的操做,java中还有Timer类一样能够实现。

Timer timer = new Timer();
timer.schedule(new TimerTask() {
    @Override
    public void run() {
        log.warn("timer run");
    }
}, new Date(), 5 * 1000);

参考资料:
深刻理解java线程池—ThreadPoolExecutor

相关文章
相关标签/搜索