一文了解线程池及实现原理

一文了解线程池及实现原理,第1张

一文了解线程池及实现原理

声明:尊重他人劳动成果,转载请附带原文链接!学习交流,仅供参考!

文章目录
    • 一、线程池介绍
        • 1、什么是线程池?
        • 2、为什么要创建线程池?
        • 3、使用线程池的好处
    • 二、创建线程池
        • 1、线程池构造函数的参数详解
        • 2、线程池应该是手动创建还是自动创建?
        • 3、线程池里的线程数量设定多少才合适?
    • 三、停止线程池
        • 停止线程池的正确方法
    • 四、线程池拒绝任务策略
    • 五、钩子方法
    • 六、实现原理、源码分析
        • 1、线程池的组成部分
        • 2、线程池、ThreadPoolExecutor、AbstractExecutorService、ExecutorService、Executor、Executors等这么多和线程池相关的类,大家都是什么关系?
        • 3、为什么线程池中相同线程能执行不同的任务?(原理解析)
        • 4、线程池的状态
        • 5、使用线程池的注意点

一、线程池介绍 1、什么是线程池?

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。

2、为什么要创建线程池?

如果每个任务都要去创建一个线程去处理,那么服务器上的资源很容易被消耗殆尽, 而使用线程池则可以减少创建线程的次数,并且还可以避
免因反复创建并销毁线程带来的开销问题。因为线程池中相同的工作线程能执行不同的任务。(为什么会这样?下文会有原理分析。)

3、使用线程池的好处
  • 加快响应速度

当任务到达时, 任务可以不需要等到线程创建就立即执行。

  • 合理利用CPU资源

通过重复利用已创建的工作线程来降低创建线程和销毁线程的开销。

  • 统一管理线程

线程是稀缺资源,使用线程池可以进行统一的分配、调优和监控

二、创建线程池 1、线程池构造函数的参数详解

  • corePoolSize

线程池中的核心线程数、不会被回收。就算它们是处于空闲状态 。除非allowCoreThreadTimeOut已设置。

注意:线程池在完成初始化后,默认情况下,线程池中并没有任何线程,线程池会等待有任务到来时,再创建新的线程去执行任务。

  • maximumPoolSize

线程池中允许的最大线程数 。
线程池有可能在核心线程数的基础上,额外添加一些线程,但是这些新增的线程数也有一个上限,这就是maximumPoolSize

误区: 这里新增的线程不是新增核心线程数。而是非核心线程数。因为上面介绍过,核心线程就算处于空闲状态也不会回收,而这些新增的这些非核心线程,当线程处于空闲状态时,则会被回收。

  • workQueue

任务存储队列。是一个阻塞队列,遵循FIFO原则先来先服务。 它的类型为:BlockingQueue

  • keepAliveTime

空闲线程–>保留的时间
也就是说,当线程池中的线程数大于核心线程数时,那么那些非核心线程,如果空闲时间超过我们设置的 keepAliveTime,那么它们就会被终止。

  • unit

keepAliveTime参数的时间单位

  • handler

线程的饱和策略,一种拒绝策略,在任务满了以后,拒绝执行某些任务。(下面会细讲线程池的拒绝策略)

  • ThreadFactory

线程工厂: 新的线程是由ThreadFactory创建的,默认使用Executors.defaultThreadFactory(),创建出来的线程都在同一个线程组,拥有同样的
NORM_PRIORITY优先级并且都不是守护线程。如果自己指定了ThreadFactory,那么就可以改变线程名、线程组、优先级、是否是守护线程等。

源码展示



三种常见的队列

  • SynchronousQueue  直接交换队列

队列中没有容量,只是用来中转,因此maximumPoolSize要设置的大一些,例如:Integer.MAX_VALUE(整数最大值)

可缓存线程池:newCachedThreadPool就是用的直接交换队列

源码展示:

  • linkedBlockingQueue   无界队列

队列不会被塞满,所以maximumPoolSize设置再大都没有什么影响,但是当任务太多,并且无法及时处理完毕时,也就是说任务堆积在队列中的时候,这样就会很容易造成占用大量的内存,可能会造成OOM。

newFixedThreadPool就是用的无界队列(自动创建)

源码展示:

  • ArrayBlockingQueue   有界队列

队列容量有上限。如果队列中的任务达到了队列上限后,就会创建非核心线程。


线程池中接收任务后的流程:

1. 当前线程数小于核心线程数corePoolSize时,即时线程池中其他线程是空闲的,它也会创建新的线程来运行新的任务。
2. 当前线程数大于核心线程数corePoolSize且小于最大线程数maximumPoolSize时,就会放入工作队列中,只有工作队列满了,才会创建新的线程。
3. 如果工作队列满了,并且当前线程数也大于或等于最大线程数maximumPoolSize。那么就会采用Handler执行拒绝策略。


增减线程的特点:

1. 如果核心线程等于最大线程数。则线程池的大小是固定的。
2. 线程池希望保持较少的线程数,只有负载变得很大时才增加。
3. 通过设置maximumPoolSize为很高的值,例如:Integer.MAX_VALUE(整数最大值),那么就可以允许线程池容纳任意数量的并发任务。
4. 只有在队列填满时,才会创建多于的非核心线程,如果使用的是无界队列。例如:linkedBlockingQueue无界队列,那么线程数就不会超过corePoolSize。

2、线程池应该是手动创建还是自动创建?

手动创建更好,因为这样可以让我们更加明确线程池的运行规则,避免资源耗尽的风向。

几种常用的自动创建线程池以及应用场景(直接调用JDK封装好的构造函数)

  • newFixedThreadPool

创建一个指定工作线程数量的线程池,可控制线程最大并发数,超出的线程在队列中等待。

应用场景: 适用于执行长期的任务,性能好很多。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ThreadPool {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            executorService.execute(new Task());
        }
    }

}
class Task implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(100);
            System.out.println(Thread.currentThread().getName() + "被执行了");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

源码分析

因为linkedBlockingQueue无界队列容量是没有上限的,这样也就造成了,当任务越来越多时,并且无法及时处理完毕时,也就是任务堆积的时候,就会很容易造成占用大量内存,就可能会导致OOM。并且它是一个固定大小的线程池。

  • newSingleThreadExecutor

创建一个单线程化的线程池,唯一工作是执行任务,保证所有任务按照指定顺序执行。

应用场景: 适用于一个任务一个任务执行的场景

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ThreadPool {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 100; i++) {
            executorService.execute(new Task());
        }
    }

}

class Task implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(100);
            System.out.println(Thread.currentThread().getName() + "被执行了");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

源码分析

可以看出,这里和newFixedThreadPool的原理是基本一样的,只不过把线程数直接设置成了1,所以这也会导致同样的问题,也就是当任务堆积的时候,也可能会占用大量的内存,也就可能会导致OOM.

  • newCachedThreadPool

可缓存线程池,无界线程池,具有自动回收非核心线程的功能。

应用场景: 适用于执行很多短期异步的小程序或者负载较轻的服务器

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ThreadPool {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 100; i++) {
            executorService.execute(new Task());
        }
    }

}

class Task implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(100);
            System.out.println(Thread.currentThread().getName() + "被执行了");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

源码分析

使用的工作的队列是SynchronousQueue直接交换队列,所以队列中是没有存储容量,并且maximumPoolSize为Integer.MAX_VALUE,则说明,来一个任务就新建一个线程,这有可能会创建非常多的线程,就有可能导致OOM。

  • newScheduledThreadPool

支持定时及周期性任务执行的线程池。

应用场景: 适用于周期性执行任务的场景。

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


public class ThreadPool {
    public static void main(String[] args) {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10);
        for (int i = 0; i < 100; i++) {
            // 1秒后执行任务,然后每隔10秒重复执行
            executorService.scheduleAtFixedRate(new Task(), 1, 10, TimeUnit.SECONDS);
        }
    }

}

class Task implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(100);
            System.out.println(Thread.currentThread().getName() + "被执行了");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

源码分析

内部使用的是DelayedWorkQueue延时队列。

JDK1.8新加的线程池:

  • workStealingPool

特点

1. 子任务
2. 窃取

四种线程池的构造函数的参数

3、线程池里的线程数量设定多少才合适?
  • CPU密集型(加密、计算hash等)

最佳线程数为CPU数的1-2倍左右。

  • 耗时IO型(读写数据库、文件、网络读写等)

最佳线程数一般大于cpu核心数很多倍,以JVM线程监控显示繁忙情况为依据,保证线程空闲可以衔接上,参考Brain Goetz推荐的计算方法:
线程数=CPU核心数*(1+平均等待时间/平均工作时间)

三、停止线程池 停止线程池的正确方法
  • shutdown()

通知一声,并不会立即停止,而是等待线程池中的任务以及工作队列的任务执行完成。执行shutdown以后就不再接收其他新的任务。否则报异常。类似interrupted()用法

代码展示

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ThreadPool {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            executorService.execute(new Task());
        }
        Thread.sleep(1000);
        // false
        System.out.println(executorService.isShutdown());
        // 执行停止
        executorService.shutdown();
        // true
        System.out.println(executorService.isShutdown());
    }
}

class Task implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(100);
            System.out.println(Thread.currentThread().getName() + "被执行了");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果

我们可以从运行结果得出,shutdown只是通知一下,并不立即停止。但是也不接收新的任务,否则会出现RejectedExecutionException异常
为了让大家详细看出不再接收新的任务,写了下面代码,

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;


public class ThreadPool {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        for (int i = 0; i < 1000000; i++) {
            executorService.execute(new Task());
        }
        Thread.sleep(1000);
        // false
        System.out.println("---------------------------------------" + executorService.isShutdown());
        // 执行停止
        executorService.shutdown();
        // true
        System.out.println("---------------------------------------" + executorService.isShutdown());
        //  执行shutdown后,不再执行新的任务 会出现RejectedExecutionException异常
        try {
            executorService.execute(new Task());
        } catch (RejectedExecutionException e) {
            System.out.println("执行了shutdown,不能再向线程池中添加新任务,并且抛出了RejectedExecutionException异常  n" + e.getMessage());
        }
    }
}
class Task implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "被执行了");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果

从结果可以看出, 执行了shutdown后,添加新的任务,就会抛出了异常。(做了捕获),程序没停止,是因为工作队列的任务还有任务没有执行完成。

  • isShutdown()

判断线程池是否进入了停止状态shutdown(), 是个boolean 类型,执行返回true,否则false。

代码展示

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ThreadPool {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            executorService.execute(new Task());
        }
        Thread.sleep(1000);
        // 返回false
        System.out.println("是否进入停止状态-----------------------> " + executorService.isShutdown());
        // 执行shutdown()
        executorService.shutdown();
        // 返回true
        System.out.println("是否进入停止状态----------------------->" + executorService.isShutdown());
    }
}

class Task implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(100);
            System.out.println(Thread.currentThread().getName() + "被执行了");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果

  • isTerminated()

判断线程池中的所有线程是否执行完毕。

注意:

当需要用到isTerminated()函数判断线程池中的所有线程是否执行完毕时候,不能直接使用该函数,必须在shutdown()方法或者shutdownNow()关闭线程池之后才能使用,否则isTerminated()永不为TRUE,线程将一直阻塞在该判断的地方,导致程序最终崩溃。


代码展示

不使用 shutdown()方法或shutdownNow() ,一直阻塞

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ThreadPool {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Task());
        }
        System.out.println(executorService.isTerminated());
        Thread.sleep(1000);
        // 让线程池停下来
//        executorService.shutdown();
//        executorService.shutdownNow();
        System.out.println(executorService.isTerminated());
    }
}

class Task implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(10);
            System.out.println(Thread.currentThread().getName() + "被执行了");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果

代码展示

使用 shutdown()方法或shutdownNow()

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ThreadPool {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Task());
        }
        System.out.println(executorService.isTerminated());
        Thread.sleep(1000);
        // 让线程池停下来  使用了shutdown()
        executorService.shutdown();
//        executorService.shutdownNow();
        System.out.println(executorService.isTerminated());
    }
}

class Task implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(10);
            System.out.println(Thread.currentThread().getName() + "被执行了");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果

  • awaitTermination(long timeout, TimeUnit unit)

阻塞到所有任务执行完成或者发生中断,那个先发生,就以那个为准,如果超过了设置的时间,就返回false,否则返回true。

代码展示

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;


public class ThreadPool {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Task());
        }
        executorService.shutdown();
        Thread.sleep(1000);
        // 是否在1s内完成
        System.out.println(executorService.awaitTermination(1000, TimeUnit.SECONDS));
    }
}
class Task implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(10);
            System.out.println(Thread.currentThread().getName() + "被执行了");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果

  • shutdownNow

立刻停止(暴力)
和shutdown一样的是一旦停止,就不能再提交新的任务。否则会出现RejectedExecutionException。但从源码分析可得,它会将工作队列中没有执行的任务用List返回来,也就是说,没有执行的任务,它会告诉我们,我们就可以做其他打算。

代码展示

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ThreadPool {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10000000; i++) {
            executorService.execute(new Task());
        }
        executorService.shutdownNow();
        // 提交新的任务
        executorService.execute(new Task());
    }
}

class Task implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "被执行了");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果


源码分析

注意返回类型

四、线程池拒绝任务策略

拒绝时机

1.当Executor关闭时,提交新任务被拒绝。

2.当Executor对最大线程和工作队列容量使用有限边界并已经饱和时。


四种拒绝策略

1.AbortPolice

直接抛出异常。默认策略。

2.DiscardPolice

​ 抛弃当前任务。不会抛出异常

3.DiscardOldestPolice

抛弃队列中最早的任务。

4.CallerRunsPolice

用调用者所在线程来执行任务。

五、钩子方法

1.可以在每个任务执行前后

2.日志、统计

代码演示

import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class ThreadPool extends ThreadPoolExecutor {
    private Lock lock = new ReentrantLock();

    public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
    }

    public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
    }

    public ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }

    // 之前执行
    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        lock.lock();
        try {
            System.out.println("开始执行");
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        // 创建线程池
        ThreadPool t = new ThreadPool(10, 10, 0L, TimeUnit.SECONDS, new linkedBlockingDeque<>());
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "我被执行了");
            }
        };
        // 执行任务
        for (int i = 0; i < 10; i++) {
            t.execute(task);
        }
    }
}

运行结果

六、实现原理、源码分析 1、线程池的组成部分
  • 线程池管理器

用于创建并管理线程池。

  • 工作线程

线程池中线程。

  • 任务接口(Task)

每个任务必须实现的接口,以供工作线程调度任务的执行。

  • 任务队列

用于存放没有处理的任务。提供一种缓冲机制。

2、线程池、ThreadPoolExecutor、AbstractExecutorService、ExecutorService、Executor、Executors等这么多和线程池相关的类,大家都是什么关系?

从源码的UML图中可以看出:

  • Executor

Executor是一个顶级接口,只有一个方法,就是执行任务的。

源码展示

  • ExecutorService

ExecutorService也是一个接口,它继承了Executor,它增加了一些初步管理线程池的方法

源码展示

  • AbstractExecutorService

AbstractExecutorService 是一个抽象类,它实现了ExecutorService接口

源码展示

  • ThreadPoolExecutor

ThreadPoolExecutor 是一个类,继承了抽象类AbstractExecutorService,用来创建线程池。

  • Executors

Executors是一个工具类,可以帮我快速创建线程池。

源码展示

3、为什么线程池中相同线程能执行不同的任务?(原理解析)

线程池里的线程在执行完你给的任务后并没有结束run方法,而是进入等待状态,进入空闲队列,这些线程除非有需要销毁,否则run方法永远不会结束

源码分析

首先检查当前线程池的工作线程数是否小于corePoolSize,如果小于的话,那么直接调用addWoker()来添加工作线程。

下面是addWorker()的具体方法

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

在addWorker()方法中,首先判断当前线程池的状态,如果状态已经不是shutdown或者running,或者已经为shutdown但是工作队列已经为空,那么这个时候直接返回添加工作线程失败。

接下来是对线程池线程数量的判断,根据调用时的core的值来判断是跟corePoolSize还是 maximumPoolSize判断。
在确认了线程池状态以及线程池中工作线程数量之后,才真正开始添加工作线程。

新建立一个worker类(线程池的内部类,具体的工作线程),将要执行的具体线程做为构造方法中的参数传递进去,接下来将其加入线程池的工作线程容器workers,并且更新工作线程最大量,最后调用worker工作线程的start()方法,就完成了工作线程的建立与启动。

让我们回到execute()方法,如果我们在一开始的线程数量就大于corePoolSize,或者我们在调用addworker()方法的过程中出现了问题导致添加工作线程数量失败,那么我们会继续执行接下来的逻辑。

在判断完毕线程池的状态后,则会将任务通过workQueue.offer())方法试图加进任务队列。
Offer()方法的具体实现会根据在线程池构造方法中选取的任务队列种类而产生变化。
但是如果成功加入了任务队列,仍旧需要注意判断如果线程池的状态,如果已经不是running那么会拒绝执行这一任务并执行相应的拒绝策略。
在最后需要记得成功加入队列成功后,如果线程池中如果已经没有了工作线程,需要重新建立一个工作线程去执行仍旧在任务队列中等待执行的任务。

如果在之前的前提下加入任务队列也失败了(比如任务队列已满),则会在不超过线程池最大线程数量的前提下建立一个工作线程来处理。
如果在最后的建立工作线程也失败了,那么我们只有很遗憾的执行任务的拒绝策略了。

在之前的过程中我们建立了工作线程Worker()类,那么我们现在看看worker类的内部实现,也可以说是线程池的核心部分。Worker类作为线程池的内部类,接下来是Worker()类的成员。
thread 作为worker的工作线程空间,由线程池中所设置的线程工厂生成。
firstTask 则是worker在构造方法中所接受到的所要执行的任务。
completedTasks 作为该worker类所执行完毕的任务总数。

接下来我们可以看最重要的,也就是我们之前建立完Worker类之后立马调用的run()方法了

run()方法实现的很简单,我们可以继续追踪下去

如果这个worker还没有执行过在构造方法就传入的任务,那么在这个方法中,会直接执行这一任务,如果没有,则会尝试去从任务队列当中去取的新的任务。
但是在真正调用任务之前,仍旧会判断线程池的状态,如果已经不是running亦或是shutdwon,则会直接确保线程被中断。如果没有,将会继续执行并确保不被中断。
接下来可见,我们所需要的任务,直接在工作线程中直接以run()方式以非线程的方式所调用,这里也就是我们所需要的任务真正执行的地方。
在执行完毕后,工作线程的使命并没有真正宣告段落。在while部分worker仍旧会通过getTask()方法试图取得新的任务。下面是getTask()的实现。

首先仍旧会判断线程池的状态是否是running还是shutdown以及stop状态下队列是否仍旧有需要等待执行的任务。

如果状态没有问题,则会跟据allowCoreThreadTimeOut和corePoolSize的值,通过对前面这两个属性解释的方式来选择从任务队列中获得任务的方式(是否设置timeout)。

其中的timedOut保证了确认前一次试图取任务时超时发生的记录,以确保工作线程的回收。
在runWorker()方法的最后,调用了processWorkerExist()方法来执行工作线程的回收。

在这一方法中,首先确保已经重新更新了线程池中工作线程的数量,之后从线程池中的工作线程容器移去当前工作线程,并且将完成的任务总数加到线程池的任务总数当中。
在最后仍旧要确保线程池中依旧存在大于等于最小线程数量的工作线程数量存在,如果没有,则重新建立工作线程去等待处理任务队列中任务。

4、线程池的状态
  • RUNNING

接受新任务并处理排队任务。

  • SHUTDOWN

不接受新任务,但是处理排队任务

  • STOP

不接受新任务、也不处理排队任务、并中断正在执行的任务。

  • TIDYING

所有任务都已终止,workerCount为零时,线程会转到TIDYING状态,并将运行terminate()钩子方法。

  • TERMINATED

terminate()运行完成。

5、使用线程池的注意点
  • 避免任务堆积
  • 避免线程数过度增加
  • 排查线程泄露

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5437711.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-11
下一篇 2022-12-11

发表评论

登录后才能评论

评论列表(0条)

保存