Java并发编程系列---线程池的实现原理

一、线程池简介

1.1 什么是线程池

所谓线程池,通俗的理解就是有一个池子,里面存放着已经创建好的线程,当有任务提交给线程池执行时,池子中的某个线程会主动执行该任务。如果池子中的线程数量不够应付数量众多的任务时,则需要自动扩充新的线程到池子中,但是该数量是有限的,就好比池塘的水界线一样。当任务比较少的时候,池子中的线程能够自动回收,释放资源。为了能够异步地提交任务和缓存未被处理的任务,需要有一个任务队列。

1.2 为什么需要线程池

自JDK1.5起,utils 包提供了ExecutorService 线程池的实现,主要目的是为了重复利用线程,提高系统效率。Thread是一 个重量级的资源,创建、启动以及销毁都是比较耗费系统资源的,因此对线程的重复利用一种是非常好的程序设计习惯,加之系统中可创建的线程数量是有限的,线程数量和系统性能是–种抛物线的关系,也就是说当线程数量达到某个数值的时候,性能反倒会降低很多,因此对线程的管理,尤其是数量的控制更能直接决定程序的性能。

1.3 线程池的好处

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统 资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做 到合理利用线程池,必须对其实现原理了如指掌。

1.4 一个完整的线程池应该具备那些要素

  1. 任务队列:用于缓存提交的任务。
  2. 线程数量管理功能:一个线程池必须能够很好地管理和控制线程数量,可通过如下三个参数来实现,比如创建线程池时初始的线程数量init;线程池自动扩充时最大的线程数量max;在线程池空闲时需要释放线程但是也要维护一定数量的活跃数量或者核心数量core。有了这三个参数,就能够很好地控制线程池中的线程数量,将其维护在一个合理的范围之内,三者之间的关系是init<=core<=max。
  3. 任务拒绝策略:如果线程数量已达到上限且任务队列已满,则需要有相应的拒绝策略来通知任务提交者。
  4. 线程工厂:主要用于个性化定制线程,比如将线程设置为守护线程以及设置线程名称等。
  5. QueueSize :任务队列主要存放提交的Runnable, 但是为了防止内存溢出,需要有limit数量对其进行控制。
  6. Keepedalive时间:该时间主要决定线程各个重要参数自动维护的时间间隔。

二、线程池的实现原理

2.1 线程池的处理流程

  1. 线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下个流程。
  2. 线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。
  3. 线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。

Java并发编程系列---线程池的实现原理_第1张图片

2.2 ThreadPoolExecutor执行execute方法

Java并发编程系列---线程池的实现原理_第2张图片

  1. 如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。
  2. 如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。
  3. 如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。 4. 如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。

源码如下:

 public void execute(Runnable command) {
     
        if (command == null)
            throw new NullPointerException();
            
        int c = ctl.get();
        //如果工作运行的线程小于corePoolSize 
        if (workerCountOf(c) < corePoolSize) {
     
        // addWorker的调用会自动地检查runState和workerCount,从而通过返回false来防止在不应该添加线程的情况下添加
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //如果我们不能加入队列任务,会尝试添加一个新的线程 如果失败了,就会拒绝任务
        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);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

你可能感兴趣的:(多线程与高并发,Thread,ThreadPool)