线程池ThreadPoolExecutor源码分析(一)

1、线程池简介

1.1 线程池的概念:

线程池就是首先创建一些线程,它们的集合称为线程池。使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。

1.2 线程池的工作机制

  • 在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程,线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程。
  • 一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。

1.3 使用线程池的好处

Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。在开发过程中,合理地使用线程池能够带来3个好处:

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

第三:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用线程池,必须对其实现原理了如指掌。

2、JDK对线程池的支持

JDK提供了Executor框架,可以让我们有效的管理和控制我们的线程,其实质也就是一个线程池。Executor下的接口和类继承关系如下:

其中,Executors相当于是一个工具类,提供了一系列静态工厂方法用于创建各种线程池:

挑几个比较重要的来看一下:

  • newFixedThreadPool:该方法返回一个固定线程数量的线程池;
  • newSingleThreadExecutor:该方法返回一个只有一个现成的线程池;
  • newCachedThreadPool:返回一个可以根据实际情况调整线程数量的线程池;
  • newSingleThreadScheduledExecutor:该方法和newSingleThreadExecutor的区别是给定了时间执行某任务的功能,可以进行定时执行等;
  • newScheduledThreadPool:在4的基础上可以指定线程数量。

 创建线程池实质调用的还是ThreadPoolExecutor

 在Executors类中,我们拿出来一个方法简单分析一下:

  可以看出,类似的其他方法一样,在Executors内部创建线程池的时候,实际创建的都是一个ThreadPoolExecutor对象,只是对ThreadPoolExecutor构造方法,进行了默认值的设定。ThreadPoolExecutor的构造方法如下:

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

方法的内容略看,仔细分析一下它的参数:

  • corePoolSize :核心线程池大小,也就是默认的工作线程数量;
  • maximumPoolSize : 线程池最大容量大小;
  • keepAliveTime :线程池空闲时,线程存活的时间;
  • TimeUnit :上一个参数的时间单位;
  • ThreadFactory :线程工厂,一般使用默认的 ;
  • BlockingQueue:阻塞任务队列,用于存放提交来的任务;
  • RejectedExecutionHandler: 线程拒绝策略;

尝试使用一下线程池:

    public static void threadPoolTest() {
        //ExecutorService threadPool = Executors.newFixedThreadPool(5);
        //ExecutorService threadPool = Executors.newSingleThreadExecutor();
        ExecutorService threadPool = Executors.newCachedThreadPool();
        for (int i = 1;i<=10;i++){
            threadPool.execute(()->{
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"\t办理业务");
            });
        }
        threadPool.shutdown();
    }

3、手写线程池

参考阿里巴巴java开发手册:

【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。 说明:使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。

【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。说明:Executors返回的线程池对象的弊端如下:
1)FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
2)CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

手写一个线程池:(不考虑拒绝策略)

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executor = new ThreadPoolExecutor(
                2,
                6,
                2,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(3),
                Executors.defaultThreadFactory()
        );

        for (int i = 1;i<5;i++){
            executor.execute(()->{
                System.out.println(Thread.currentThread().getName()+"\t办理业务");
            });
        }
        executor.shutdown();
    }}

其实线程提交可以使用executesubmit这两种方法,但是两者有所不同:

(1)execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功。通过以下代码可知execute()方法输入的任务是一个Runnable类的实例。

(2)submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。

4、线程池的核心原理

先从一张看起,详细描述了一个任务的执行情况和线程池的工作过程:

我们来解释一下整个流程:

1)在创建了线程池之后等待提交任务请求过来;

2)当调用了execute()方法提交一个任务请求的时候,线程池会执行以下的判断:

  • 如果正在运行的线程数量小于corePoolSize,那么马上创建线程执行这个任务;
  • 如果正在运行的线程数量大于等于corePoolSize,那么将这个任务放入任务队列中;
  • 如果此时任务队列已经满了,且正在运行的线程小于maxnumPoolSize,那么还是要创建非核心线程去执行任务;
  • 如果队列已经满了,且正在运行的线程数量已经达到了最大maxnumPoolSize,那么线程池会启动饱和拒绝策略来执行;

3)当一个线程完成任务的时候,它会从队列中取出下一个任务来执行;

4)当一个线程无事可做超过keepAliveTime时,线程会停掉大于corePoolSize数量的线程。

5、JDK内置拒绝策略

什么是拒绝策略呢?当等待队列也已经排满了,再也塞不下新的任务了同时,线程池的max也到达了,无法接续为新任务服务,这时我们需要拒绝策略机制合理的处理这个问题。

JDK内置了四种常用的拒绝策略:

  • AbortPolicy:它是默认的拒绝策略,直接抛出RejectedException异常阻止系统正常的运行;
  • CallerRunPolicy:调用者运行策略,该策略不会抛弃任务,也不会抛出异常,而是将任务返回给调用者执行,比如main线程提交的任务,就会被返回给main线程执行;
  • DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入到队列中再次尝试提交;
  • DiscardPolicy:直接丢弃任务,不予处理,不抛出异常。


    参考资料:

原文地址:https://www.cnblogs.com/simon-1024/p/12215548.html

时间: 2024-10-10 13:30:19

线程池ThreadPoolExecutor源码分析(一)的相关文章

java线程池ThreadPoolExector源码分析

java线程池ThreadPoolExector源码分析 今天研究了下ThreadPoolExector源码,大致上总结了以下几点跟大家分享下: 一.ThreadPoolExector几个主要变量 先了解下ThreadPoolExector中比较重要的几个变量.  corePoolSize:核心线程数量     maximumPoolSize:最大线程数量 allowCoreThreadTimeOut:是否允许线程超时(设置为true时与keepAliveTime,TimeUnit一起起作用)

【Java并发编程】21、线程池ThreadPoolExecutor源码解析

一.前言 JUC这部分还有线程池这一块没有分析,需要抓紧时间分析,下面开始ThreadPoolExecutor,其是线程池的基础,分析完了这个类会简化之后的分析,线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行任务集时使用的线程)的方法.下面开始分析. 二.ThreadPoolExecutor数据结构 在ThreadPoolExecutor的内部,主要由BlockingQueue和AbstractQu

线程池ThreadPoolExecutor源码解读研究(JDK1.8)

一.什么是线程池 为什么要使用线程池?在多线程并发开发中,线程的数量较多,且每个线程执行一定的时间后就结束了,下一个线程任务到来还需要重新创建线程,这样线程数量特别庞大的时候,频繁的创建线程和销毁线程需要一定时间而且增加系统的额外开销.基于这样的场景,线程池就出现了,线程池可以做到一个线程的任务处理完可以接受下一个任务,并不需要频繁的创建销毁,这样大大节省了时间和系统的开销. 线程池,顾名思义,就是一个池子,任务提交的到线程池后,线程池会在池子里边找有没有空闲的线程,如果没有,就会进入等待状态,

ThreadPoolExecutor(线程池)源码分析

1. 常量和变量 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); // 高3位为线程池的运行状态,低29位为当前线程总数 private static final int COUNT_BITS = Integer.SIZE - 3; // 32 -3 = 29 private static final int CAPACITY = (1 << COUNT_BITS) - 1; // 线程池容量:2^

[转载] Java线程池框架源码分析

转载自http://www.linuxidc.com/Linux/2014-11/108791.htm 相关类Executor,Executors,AbstractExecutorService,ExecutorService Executor:整个线程池执行者框架的顶层接口.定义了一个execute方法,整个线程执行者框架的核心方法. public interface Executor { void execute(Runnable command);} ExecutorService:这是一

java线程池框架源码分析

相关类Executor,Executors,AbstractExecutorService,ExecutorService Executor:整个线程池执行者框架的顶层接口.定义了一个execute方法,整个线程执行者框架的核心方法. public interface Executor { void execute(Runnable command); } ExecutorService:这是一个接口它继承自Executor,定义了shutdown,shutdownNow,awaitTermin

Java线程池ThreadPoolExecutor使用和分析(三) - 终止线程池原理

相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池ThreadPoolExecutor使用和分析(三) - 终止线程池原理 以下是本文的目录大纲: 一.shutdown()  --  温柔的终止线程池 interruptIdleWorkers()  --  中断空闲worker tryTerminate()  --  尝试终止线程池 二.shutdown

Java并发包源码学习之线程池(一)ThreadPoolExecutor源码分析

Java中使用线程池技术一般都是使用Executors这个工厂类,它提供了非常简单方法来创建各种类型的线程池: public static ExecutorService newFixedThreadPool(int nThreads) public static ExecutorService newSingleThreadExecutor() public static ExecutorService newCachedThreadPool() public static Scheduled

Java多线程 -- JUC包源码分析11 -- ThreadPoolExecutor源码分析

在JUC包中,线程池部分本身有很多组件,可以说是前面所分析的各种技术的一个综合应用.从本文开始,将综合前面的知识,逐个分析线程池的各个组件. -Executor/Executors -ThreadPoolExecutor使用介绍 -ThreadPoolExecutor实现原理 –ThreadPoolExecutor的中断与优雅关闭 shutdown + awaitTermination –shutdown的一个误区 Executor/Executors Executor是线程池框架最基本的几个接