精华内容
下载资源
问答
  • 线程池线程复用原理

    千次阅读 2020-06-29 09:36:29
    线程池线程复用原理 文章目录线程池线程复用原理前言项目环境1.什么是线程复用?2.线程复用的原理3.线程池执行流程3.1 流程图3.2 线程创建的流程3.3 ThreadPoolExecutor#execute 源码分析3.4 逐行分析4.线程复用...

    线程池的线程复用原理

    前言

    第十一篇:线程池使用及源码分析 中有简单介绍过线程复用的基本原理,本章会进行更为详细的讨论。

    项目环境

    1.什么是线程复用?

    在线程池中,通过同一个线程去执行不同的任务,这就是线程复用。

    假设现在有 100 个任务,我们创建一个固定线程的线程池(FixedThreadPool),核心线程数和最大线程数都是 3,那么当这个 100 个任务执行完,都只会使用三个线程。

    示例:

    public class FixedThreadPoolDemo {
    
        static ExecutorService executorService = Executors.newFixedThreadPool(3);
    
        public static void main(String[] args) {
            for (int i = 0; i < 100; i++) {
                executorService.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "-> 执行");
                });
            }
            // 关闭线程池
            executorService.shutdown();
        }
    
    }
    

    执行结果:

    pool-1-thread-1-> 执行
    pool-1-thread-2-> 执行
    pool-1-thread-3-> 执行
    pool-1-thread-1-> 执行
    pool-1-thread-3-> 执行
    pool-1-thread-2-> 执行
    pool-1-thread-3-> 执行
    pool-1-thread-1-> 执行
    ...
    

    2.线程复用的原理

    线程池将线程和任务进行解耦,线程是线程,任务是任务,摆脱了之前通过 Thread 创建线程时的一个线程必须对应一个任务的限制。

    在线程池中,同一个线程可以从阻塞队列中不断获取新任务来执行,其核心原理在于线程池对 Thread 进行了封装,并不是每次执行任务都会调用 Thread.start() 来创建新线程,而是让每个线程去执行一个“循环任务”,在这个“循环任务”中不停的检查是否有任务需要被执行,如果有则直接执行,也就是调用任务中的 run 方法,将 run 方法当成一个普通的方法执行,通过这种方式将只使用固定的线程就将所有任务的 run 方法串联起来。

    3.线程池执行流程

    这部分内容在 Java 线程池的各个参数的含义 讨论过,这里我们再复习一次,再从中去了解线程复用。

    3.1 流程图

    在这里插入图片描述

    3.2 线程创建的流程

    • 当任务提交之后,线程池首先会检查当前线程数,如果当前的线程数小于核心线程数(corePoolSize),比如最开始创建的时候线程数为 0,则新建线程并执行任务。
    • 当提交的任务不断增加,创建的线程数等于核心线程数(corePoolSize),新增的任务会被添加到 workQueue 任务队列中,等待核心线程执行完当前任务后,重新从 workQueue 中获取任务执行。
    • 假设任务非常多,达到了 workQueue 的最大容量,但是当前线程数小于最大线程数(maximumPoolSize),线程池会在核心线程数(corePoolSize)的基础上继续创建线程来执行任务。
    • 假设任务继续增加,线程池的线程数达到最大线程数(maximumPoolSize),如果任务继续增加,这个时候线程池就会采用拒绝策略来拒绝这些任务。

    在任务不断增加的过程中,线程池会逐一进行以下 4 个方面的判断

    • 核心线程数(corePoolSize)
    • 任务队列(workQueue)
    • 最大线程数(maximumPoolSize)
    • 拒绝策略

    3.3 ThreadPoolExecutor#execute 源码分析

    • java.util.concurrent.ThreadPoolExecutor#execute
        public void execute(Runnable command) {
            // 如果传入的Runnable的空,就抛出异常
            if (command == null)
                throw new NullPointerException();
            int c = ctl.get();
            // 线程池中的线程比核心线程数少 
            if (workerCountOf(c) < corePoolSize) {
                // 新建一个核心线程执行任务
                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);
        }
    

    3.4 逐行分析

    //如果传入的Runnable的空,就抛出异常        
    if (command == null)
       throw new NullPointerException();
    

    execute 方法中通过 if 语句判断 command ,也就是 Runnable 任务是否等于 null,如果为 null 就抛出异常。

    if (workerCountOf(c) < corePoolSize) { 
        if (addWorker(command, true)) 
            return;
            c = ctl.get();
    }
    

    判断当前线程数是否小于核心线程数,如果小于核心线程数就调用 addWorker() 方法增加一个 Worker,这里的 Worker 就可以理解为一个线程。

    addWorker 方法的主要作用是在线程池中创建一个线程并执行传入的任务,如果返回 true 代表添加成功,如果返回 false 代表添加失败。

    • 第一个参数表示传入的任务

    • 第二个参数是个布尔值,如果布尔值传入 true 代表增加线程时判断当前线程是否少于 corePoolSize,小于则增加新线程(核心线程),大于等于则不增加;同理,如果传入 false 代表增加线程时判断当前线程是否少于 maximumPoolSize,小于则增加新线程(非核心线程),大于等于则不增加,所以这里的布尔值的含义是以核心线程数为界限还是以最大线程数为界限进行是否新增非核心线程的判断

    这一段判断相关源码如下

        private boolean addWorker(Runnable firstTask, boolean core) {     
                    ...
                    int wc = workerCountOf(c);//当前工作线程数
                    //判断当前工作线程数>=最大线程数 或者 >=核心线程数(当core = true)
                    if (wc >= CAPACITY ||
                        wc >= (core ? corePoolSize : maximumPoolSize))
                        return false;
                    ...
    

    最核心的就是 core ? corePoolSize : maximumPoolSize 这个三目运算。

            // 核心线程已满,但是任务队列未满,添加到队列中
            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);
            }
    

    如果代码执行到这里,说明当前线程数大于或等于核心线程数或者 addWorker 失败了,那么就需要通过

    if (isRunning(c) && workQueue.offer(command)) 检查线程池状态是否为 Running,如果线程池状态是 Running 就通过 workQueue.offer(command) 将任务放入任务队列中,

    任务成功添加到队列以后,再次检查线程池状态,如果线程池不处于 Running 状态,说明线程池被关闭,那么就移除刚刚添加到任务队列中的任务,并执行拒绝策略,代码如下:

                if (! isRunning(recheck) && remove(command))
                    // 如果线程池处于非运行状态,并且把当前的任务从任务队列中移除成功,则拒绝该任务
                    reject(command);
    

    下面我们再来看后一个 else 分支:

                else if (workerCountOf(recheck) == 0)
                    // 如果之前的线程已经被销毁完,新建一个非核心线程
                    addWorker(null, false);
    

    进入这个 else 说明前面判断到线程池状态为 Running,那么当任务被添加进来之后就需要防止没有可执行线程的情况发生(比如之前的线程被回收了或意外终止了),所以此时如果检查当前线程数为 0,也就是 workerCountOf(recheck) == 0,那就执行 addWorker() 方法新建一个非核心线程。

    我们再来看最后一部分代码:

            // 核心线程池已满,队列已满,尝试创建一个非核心新的线程
            else if (!addWorker(command, false))
                // 如果创建新线程失败,说明线程池关闭或者线程池满了,拒绝任务
                reject(command);
    

    执行到这里,说明线程池不是 Running 状态,又或者线程数 >= 核心线程数并且任务队列已经满了,根据规则,此时需要添加新线程,直到线程数达到“最大线程数”,所以此时就会再次调用 addWorker 方法并将第二个参数传入 false,传入 false 代表增加非核心线程。

    addWorker 方法如果返回 true 代表添加成功,如果返回 false 代表任务添加失败,说明当前线程数已经达到 maximumPoolSize,然后执行拒绝策略 reject 方法。

    如果执行到这里线程池的状态不是 Running,那么 addWorker 会失败并返回 false,所以也会执行拒绝策略 reject 方法。

    4.线程复用源码分析

    • java.util.concurrent.ThreadPoolExecutor#runWorker

    省略掉部分和复用无关的代码之后,代码如下:

        final void runWorker(Worker w) {
            Thread wt = Thread.currentThread();
            Runnable task = w.firstTask;
            w.firstTask = null;
            w.unlock(); // 释放锁 设置work的state=0 允许中断
            boolean completedAbruptly = true;
            try {
                //一直执行 如果task不为空 或者 从队列中获取的task不为空
                while (task != null || (task = getTask()) != null) {
                        task.run();//执行task中的run方法
                    }
                }
                completedAbruptly = false;
            } finally {
                //1.将 worker 从数组 workers 里删除掉
                //2.根据布尔值 allowCoreThreadTimeOut 来决定是否补充新的 Worker 进数组 workers
                processWorkerExit(w, completedAbruptly);
            }
        }
    

    可以看到,实现线程复用的逻辑主要在一个不停循环的 while 循环体中。

    • 通过获取 Worker 的 firstTask 或者通过 getTask 方法从 workQueue 中获取待执行的任务

    • 直接通过 task.run() 来执行具体的任务(而不是新建线程)

    在这里,我们找到了线程复用最终的实现,通过取 Worker 的 firstTask 或者 getTask 方法从 workQueue 中取出了新任务,并直接调用 Runnable 的 run 方法来执行任务,也就是如之前所说的,每个线程都始终在一个大循环中,反复获取任务,然后执行任务,从而实现了线程的复用。

    展开全文
  • 线程复用原理 ...但无论是固定数量或可变数量的线程,其线程数量都远远小于任务数量,面对这种情况线程池可以通过线程复用让同一个线程去执行不同的任务,那么线程复用是如何实现的呢? ...

    线程复用原理

    我们知道线程池会使用固定数量或可变数量的线程来执行任务,但无论是固定数量或可变数量的线程,其线程数量都远远小于任务数量,面对这种情况线程池可以通过线程复用让同一个线程去执行不同的任务,那么线程复用是如何实现的呢?

    线程池可以把线程和任务进行解耦,线程归线程,任务归任务,摆脱了之前通过 Thread 创建线程时的一个线程必须对应一个任务的限制。在线程池中,同一个线程可以从 BlockingQueue 中不断提取新任务来执行,其核心原理在于线程池对 Thread 进行了封装,并不是每次执行任务都会调用 Thread.start() 来创建新线程,而是让每个线程去执行一个“循环任务”,在这个“循环任务”中,不停地检查是否还有任务等待被执行,如果有则直接去执行这个任务,也就是调用任务的 run 方法,把 run 方法当作和普通方法一样的地位去调用,相当于把每个任务的 run() 方法串联了起来,所以线程数量并不增加。

    我们首先来复习一下线程池创建新线程的时机和规则:

    在这里插入图片描述
    如流程图所示,当提交任务后,线程池首先会检查当前线程数,如果此时线程数小于核心线程数,比如最开始线程数量为 0,则新建线程并执行任务,随着任务的不断增加,线程数会逐渐增加并达到核心线程数,此时如果仍有任务被不断提交,就会被放入 workQueue 任务队列中,等待核心线程执行完当前任务后重新从 workQueue 中提取正在等待被执行的任务。此时,假设我们的任务特别的多,已经达到了 workQueue 的容量上限,这时线程池就会启动后备力量,也就是 maxPoolSize 最大线程数,线程池会在 corePoolSize 核心线程数的基础上继续创建线程来执行任务,假设任务被不断提交,线程池会持续创建线程直到线程数达到 maxPoolSize 最大线程数,如果依然有任务被提交,这就超过了线程池的最大处理能力,这个时候线程池就会拒绝这些任务,我们可以看到实际上任务进来之后,线程池会逐一判断 corePoolSize 、workQueue 、maxPoolSize ,如果依然不能满足需求,则会拒绝任务。

    我们接下来具体看看代码是如何实现的,我们从 execute 方法开始分析,源码如下所示。

    public void execute(Runnable command) { 
        if (command == null) 
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) { 
            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);
    }
    
    

    线程复用源码解析

    这段代码短小精悍,内容丰富,接下来我们具体分析代码中的逻辑,首先看下前几行:

    //如果传入的Runnable的空,就抛出异常
    if (command == null) 
        throw new NullPointerException();
    
    

    execute 方法中通过 if 语句判断 command ,也就是 Runnable 任务是否等于 null,如果为 null 就抛出异常。

    接下来判断当前线程数是否小于核心线程数,如果小于核心线程数就调用 addWorker() 方法增加一个 Worker,这里的 Worker 就可以理解为一个线程:

    if (workerCountOf(c) < corePoolSize) { 
        if (addWorker(command, true)) 
            return;
            c = ctl.get();
    }
    
    

    那 addWorker 方法又是做什么用的呢?addWorker 方法的主要作用是在线程池中创建一个线程并执行第一个参数传入的任务,它的第二个参数是个布尔值,如果布尔值传入 true 代表增加线程时判断当前线程是否少于 corePoolSize,小于则增加新线程,大于等于则不增加;同理,如果传入 false 代表增加线程时判断当前线程是否少于 maxPoolSize,小于则增加新线程,大于等于则不增加,所以这里的布尔值的含义是以核心线程数为界限还是以最大线程数为界限进行是否新增线程的判断。addWorker() 方法如果返回 true 代表添加成功,如果返回 false 代表添加失败。

    我们接下来看下一部分代码:

    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);
    }
    
    

    如果代码执行到这里,说明当前线程数大于或等于核心线程数或者 addWorker 失败了,那么就需要通过 if (isRunning© && workQueue.offer(command)) 检查线程池状态是否为 Running,如果线程池状态是 Running 就把任务放入任务队列中,也就是 workQueue.offer(command)。如果线程池已经不处于 Running 状态,说明线程池被关闭,那么就移除刚刚添加到任务队列中的任务,并执行拒绝策略,代码如下所示:

    if (! isRunning(recheck) && remove(command)) 
        reject(command);
    
    

    下面我们再来看后一个 else 分支:

    else if (workerCountOf(recheck) == 0) 
        addWorker(null, false);
    
    

    能进入这个 else 说明前面判断到线程池状态为 Running,那么当任务被添加进来之后就需要防止没有可执行线程的情况发生(比如之前的线程被回收了或意外终止了),所以此时如果检查当前线程数为 0,也就是 workerCountOf(recheck) == 0,那就执行 addWorker() 方法新建线程。

    我们再来看最后一部分代码:

    else if (!addWorker(command, false)) 
        reject(command);
    
    

    执行到这里,说明线程池不是 Running 状态或线程数大于或等于核心线程数并且任务队列已经满了,根据规则,此时需要添加新线程,直到线程数达到“最大线程数”,所以此时就会再次调用 addWorker 方法并将第二个参数传入 false,传入 false 代表增加线程时判断当前线程数是否少于 maxPoolSize,小于则增加新线程,大于等于则不增加,也就是以 maxPoolSize 为上限创建新的 worker;addWorker 方法如果返回 true 代表添加成功,如果返回 false 代表任务添加失败,说明当前线程数已经达到 maxPoolSize,然后执行拒绝策略 reject 方法。如果执行到这里线程池的状态不是 Running,那么 addWorker 会失败并返回 false,所以也会执行拒绝策略 reject 方法。

    可以看出,在 execute 方法中,多次调用 addWorker 方法把任务传入,addWorker 方法会添加并启动一个 Worker,这里的 Worker 可以理解为是对 Thread 的包装,Worker 内部有一个 Thread 对象,它正是最终真正执行任务的线程,所以一个 Worker 就对应线程池中的一个线程,addWorker 就代表增加线程。线程复用的逻辑实现主要在 Worker 类中的 run 方法里执行的 runWorker 方法中,简化后的 runWorker 方法代码如下所示。

    runWorker(Worker w) {
        Runnable task = w.firstTask;
        while (task != null || (task = getTask()) != null) {
            try {
                task.run();
            } finally {
                task = null;
            }
        }
    }
    
    

    可以看出,实现线程复用的逻辑主要在一个不停循环的 while 循环体中。

    1. 通过取 Worker 的 firstTask 或者通过 getTask 方法从 workQueue 中获取待执行的任务。
    2. 直接调用 task 的 run 方法来执行具体的任务(而不是新建线程)。

    在这里,我们找到了最终的实现,通过取 Worker 的 firstTask 或者 getTask方法从 workQueue 中取出了新任务,并直接调用 Runnable 的 run 方法来执行任务,也就是如之前所说的,每个线程都始终在一个大循环中,反复获取任务,然后执行任务,从而实现了线程的复用。

    展开全文
  • 实际上本人没有实战地使用过线程池,写这篇文章仅仅是出于对线程复用的兴趣。 本文将通过在网上搜集到的资料以及自己编写的简易版的线程池来说明线程复用技术。 二、线程池的概念 线程池是一种提高系统性能的技术,...

    一、行文介绍

    实际上本人没有实战地使用过线程池,写这篇文章仅仅是出于对线程复用的兴趣。
    本文将通过在网上搜集到的资料以及自己编写的简易版的线程池来说明线程复用技术。

    二、线程池的概念

    线程池是一种提高系统性能的技术,可以创建一定数量的工作线程,当向线程池提交任务时,线程池中的空闲线程将会接收任务并执行,执行完之后线程并不会马上关闭,而是处于空闲状态,等待下一个任务的到达。
    在这里执行任务避免了大量线程的创建与销毁工作,从而提高了系统效率。

    三、线程池的原理

    线程池的关键技术是 线程复用 ,如何实现线程复用呢?
    我们可以这样理解,现在线程池中创建了数量一定的线程,并start(),每个线程进入一个循环体,循环体内线程请求一个 阻塞队列 获取一个Runnable对象,然后并不是像启动线程那样启动新的线程,而是直接调用该对象的run()方法。
    这样,只要阻塞队列里有元素,那么线程池中的线程就能够拿到一个元素,然后调用该对象的run()方法,每一次执行就相当于开启了一个新的线程,而实际上仅仅是 复用 了线程池里的线程。

    四、阻塞队列介绍

    这里简单介绍一下什么是阻塞队列,有强烈学习欲望的同学建议网上搜类似内容。
    阻塞队列,它本质上是一个队列,遵循着FIFO原则,但它实现了BlockingQueue接口,多了两个方法:

    public interface BlockingQueue<E> extends Queue<E> {
        void put(E var1) throws InterruptedException;
    
        E take() throws InterruptedException;
    }
    

    这里仅仅截取需要了解的源码。
    put()take(),前者 阻塞地 入队,后者 阻塞地 出队。

    五、概念科普

    阻塞是什么意思?
    简单地说,就是当线程遇到锁或者其他原因导致的线程无法继续运行了,就称为阻塞,等到某一通知,线程才会从阻塞队列移除。

    六、简单的线程池模型实现

    先定义线程池的接口:

    public interface ThreadPool {
    	//提交任务
        void execute(Runnable target);
    	//返回线程池线程的数量
        int getPoolSize();
    }
    

    创建线程池的工厂模式:

    public class ThreadPoolFactor{
    	//返回一个线程池
        public static MyThreadPool getMyThreadPool(){
            return new MyThreadPool(20,new BlockingArrayQueue<>(10));
        }
    }
    

    简易版的线程池类:

    public class MyThreadPool implements ThreadPool {
    
        //当前线程池里的线程
        private ArrayList<Thread> workers;
        //当前线程池的线程数量,实际上在这里这个变量没意义
        private volatile int poolSize;
        //当前线程池的最大数量
        private volatile int maxPoolSize;
        //线程池数量的默认值,实际上没有定义无参构造函数,在这里仅仅作为下界使用
        private static final int DEFAULT_CAPACITY = 10;
        //阻塞式的任务队列
        private BlockingQueue<Runnable> workQueue;
        //可重入锁,实际上在这里也是没有意义
        private static final ReentrantLock lock = new ReentrantLock();
    
        //避免麻烦,全部都先定义好
        public MyThreadPool(int maxPoolSize, BlockingQueue<Runnable> workQueue) {
            if (maxPoolSize < DEFAULT_CAPACITY) {
                maxPoolSize = DEFAULT_CAPACITY;
            }
            this.workers = new ArrayList<Thread>(maxPoolSize);
            this.maxPoolSize = maxPoolSize;
            this.workQueue = workQueue;
            for (int i = 0; i < maxPoolSize; i++) {
                Thread worker = new Thread(new Worker(workQueue));
                workers.add(worker);
                worker.start();
            }
        }
    
        @Override
        public void execute(Runnable target) {
        	//把任务提交到任务队列,这里没有使用put()是因为会报错,原因暂不明
            workQueue.offer(target);
        }
    
        @Override
        public int getPoolSize() {
            return poolSize;
        }
    
        private static final class Worker implements Runnable {
            Runnable target;
            BlockingQueue workQueue;
    
            public Worker(BlockingQueue workQueue) {
                this.workQueue = workQueue;
            }
    
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "---->" + "start()");
                //此线程将不会停止,当然这么粗暴的方式是因为没有考虑其他情况
                while (true) {
                    try {
                    	//阻塞式地取任务,当队列为空时,当前线程将阻塞等待
                        target = (Runnable) workQueue.take();
                        //执行任务
                        target.run();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    编写测试类:

    public class ThreadPoolTest {
        public static void main(String[] args) throws InterruptedException {
        	//创建线程池
            ThreadPool threadPool = ThreadPoolFactor.getMyThreadPool();
            //每个一秒,往线程池里扔1个任务,一共1000个
            for (int i=0;i<1000;i++){
                int finalI = i;
                Thread.sleep(1000);
                threadPool.execute(new Runnable() {
                    @Override
                    public void run() {
                    	//任务就是打印当前线程名和此时执行的任务的ID
                        System.out.println(Thread.currentThread().getName()+"---->now-->"+ finalI);
                    }
                });
            }
        }
    }
    

    输出结果:
    1.创建了20个线程并且全部都运行了

    Thread-17---->start()
    Thread-19---->start()
    Thread-10---->start()
    Thread-18---->start()
    Thread-0---->start()
    Thread-3---->start()
    Thread-12---->start()
    Thread-5---->start()
    Thread-11---->start()
    Thread-7---->start()
    Thread-13---->start()
    Thread-1---->start()
    Thread-14---->start()
    Thread-4---->start()
    Thread-8---->start()
    Thread-16---->start()
    Thread-6---->start()
    Thread-9---->start()
    Thread-2---->start()
    Thread-15---->start()
    

    2.执行任务的部分截取

    Thread-10---->now-->0
    Thread-3---->now-->1
    Thread-19---->now-->2
    Thread-18---->now-->3
    Thread-0---->now-->4
    Thread-12---->now-->5
    Thread-5---->now-->6
    Thread-17---->now-->7
    Thread-11---->now-->8
    Thread-7---->now-->9
    Thread-13---->now-->10
    Thread-1---->now-->11
    Thread-14---->now-->12
    Thread-4---->now-->13
    Thread-8---->now-->14
    Thread-16---->now-->15
    Thread-6---->now-->16
    Thread-9---->now-->17
    Thread-2---->now-->18
    Thread-15---->now-->19
    Thread-10---->now-->20
    Thread-3---->now-->21
    Thread-19---->now-->22
    Thread-5---->now-->46
    Thread-17---->now-->47
    

    七、分析总结

    从程序执行结果来看:

    Thread-5---->now-->46
    Thread-5---->now-->6
    

    可以看出,同样的线程执行了不同的任务,可以说明,此程序已经基本上实现了线程的复用了。
    之前接触过数据库连接池,使用的是代理模式,这一次想试着使用代理+反射来实现线程池,发现虽然能够实现相同的功能,但是却会报错,经过几天的查找资料,最终是使用了这一种实现方式。

    参考.

    展开全文
  • 线程池线程复用原理 线程池将线程和任务进行解耦,线程是线程,任务是任务,摆脱了之前通过Thread创建线程的一个线程必须对应一个任务的限制。 在线程池中,同一个线程可以从阻塞队列中不断获取新任务来执行,其...

    线程池中线程复用原理
    线程池将线程和任务进行解耦,线程是线程,任务是任务,摆脱了之前通过Thread创建线程的一个线程必须对应一个任务的限制。
    在线程池中,同一个线程可以从阻塞队列中不断获取新任务来执行,其核心原理在于线程池对Thread进行了封装,并不是每次执行任务都会调用Thread.start()来创建新的线程,而是让每个线程去执行一个“循环任务”,在这个“循环任务”中不停检查是否有任务需要被执行,如果有着直接执行,也就是调用任务的run方法,将run方法当成一个普通的方法执行,通过这种方式只使用固定的线程就将所有任务的run方法串联起来。

    展开全文
  • Java线程池-线程复用原理(白话篇)

    千次阅读 热门讨论 2017-10-26 00:33:55
    随便吐槽两句 今天无意中手贱看了点线程池原理的资料,结果被线程池里的...自己想不明白,上度娘上去找一找呗,结果让我的郁闷指数更上多层楼,度娘的线程池线程复用原理相关“奶水”大致也就两个味道:模棱两可和高深
  • 本章主要讲解我们如何通过真实业务情况定制合适的线程池,如何关闭线程池,线程复用原理 线程数量的判断 在我们准备根据业务环境定义一个线程池时,需要根据业务来判断一个任务执行的速度,耗费的性能。然后来确定线程...
  • 前两天和粉丝聊天的时候,粉丝问了我一个挺有意思的问题,说他之前在面试的时候被问到线程池线程复用原理,当时我跟他简单的说了一下,没想到过了几天又来问我这个问题了,说他最近又被问到了这个问题…想了想,...
  • JAVA 线程池如何实现线程复用的?
  • 线程池线程和任务进行解耦,摆脱了之前通过Thread创建线程时一个线程必须对应一个任务的限制。 在线程池中,同一个线程可以从阻塞队列中不断获取任务来执行,其核心原理在于线程池对Thread进行了封装,并不是每次...
  • 1:线程复用。重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。 2:能有效控制线程池的最大并发数,避免大量线程之间因互相抢夺系统资源而导致的阻塞现象。 3:能够对线程进行简单的管理,并提供...
  • 线程池实现线程复用”的原理? 线程复用原理 我们知道线程池会使用固定数量或可变数量的线程来执行任务,但无论是固定数量或可变数量的线程,其线程数量都远远小于任务数量,面对这种情况线程池可以通过线程复用让...
  • 需要适应一下新环境,另外新公司正好赶上了几个比较忙的项目,每天晚上到家就比较晚了,实在是分身乏术,不过该更新还是要更新滴,不然也对不起关注我的粉丝们,今天就来讲一下线程池复用原理吧,希望能对你有所...
  • 线程池线程复用,个人理解

    千次阅读 2018-05-23 10:28:04
    一个线程一般在执行完任务后就结束了,怎么再让他执行下一个任务呢其实,原理是: run( ){ while(true){ if(没有任务){wait( );} 否则 从任务队列取出任务,执行//这样执行完任务后,又回到头,问有没有任务,...
  • 实现线程复用,无须反复创建线程而消耗过多的资源,提高响应速度 可用集合来实现 容器->集合(ArrayList,HashSet,LinkedList< Thread >,HashMap) 原理: 当程序第一次启动时,创建多个线程,使用时...
  • 引言 ...那是在线程池中线程就只有那些,肯定是要做到线程的重复利用,才能执行超过线程的任务量的,那么线程池是怎么做到"线程复用"的呢? 从源码开始逐步分析,从execute()方法开始 //原子变量 ...
  • 我们知道线程池会使用固定数量或可变数量的线程来执行任务,但无论是固定数量或可变数量的线程,其线程数量都远远小于任务数量,面对这种情况线程池可以通过线程复用让同一个线程去执行不同的任务,那么线程复用背后...
  • 前几篇: Java多线程编程-(1)-线程安全和锁Synchronized概念 Java多线程编程-(2)-可重入锁以及Synchronized的其他...Java多线程编程-(5)-使用Lock对象实现同步以及线程间通信 Java多线程编程-(6)-两种...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 52,221
精华内容 20,888
关键字:

线程池如何实现线程复用