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

    千次阅读 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 方法来执行任务,也就是如之前所说的,每个线程都始终在一个大循环中,反复获取任务,然后执行任务,从而实现了线程的复用。

    展开全文
  • 使用线程池创建线程

    千次阅读 2019-03-10 05:22:26
    使用线程池创建线程的好处 阿里巴巴Java规约推荐使用线程池来创建线程而不是显式的创建线程,使用线程池来创建线程有如下好处 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销. 线程是轻量级的...

    使用线程池创建线程的好处

    阿里巴巴Java规约推荐使用线程池来创建线程而不是显式的创建线程,使用线程池来创建线程有如下好处

    • 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销.

      线程是轻量级的进程,虽然创建和销毁的开销比进程小得多,但仍是一笔不小的开销

    • 能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象
    • 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能

    Android中的线程池的概念来源于Java中的Executor,Executor是一个接口,真正的线程池的实现为ThreadPoolExecutor.

    ThreadPoolExecutor提供了一系列参数来配置线程池,通过不同的参数可以创建不同的线程池,从线程池的功能特性上来说,Android的线程池主要分为4类,这4类线程池可以通过Executors所提供的工厂方法来得到

    Android中的线程池都是直接或者间接通过配置ThreadPoolExecutor来实现的

    ThreadPoolExecutor

    public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  ThreadFactory threadFactory)
    

    各个参数代表的含义

    • corePoolSize

      线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态.如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔由keepAliveTime所指定,当等待时间超过keepAliveTime所指定的时长后,核心线程就会被终止.

    • maximumPoolSize

      线程池所能容纳的最大线程数,当活动线程数达到这个数值后,后续的新任务将会被阻塞

    • keepAliveTime

      非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收,当ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true时,keepAliveTime同样会作用于核心线程

    • unit

      用于指定keepAliveTime参数的时间单位

    • workQueue

      阻塞队列,线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中

    • threadFactory

      线程工厂,为线程池提供创建新线程的功能,ThreadFactory是一个接口,它只有一个方法:Thread newThread(Runnable r)

    • RejectedExecutionHandler

      不常用,当线程池无法执行新任务时,这可能是由于任务队列已满或者是无法成功执行任务,这个时候ThreadPoolExecutor会调用handler的rejectedExecution方法来通知调用者,默认情况下rejectedExecution方法会直接抛出一个RejectedExecutionException,RejectedExecutionHandler提供了几个可选值:CallerRunsPolicy,AbortPolicy,DiscardPolicy和DiscardOldestPolicy,其中AbortPolicy是默认值,它会直接抛出RejectedExecutionException

    线程池的分类

    • FixedThreadPool

    通过Executors的newFixedThreadPool方法来创建.它是一种线程数量固定的线程池,当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了.当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来.由于FixedThreadPool只有核心线程并且这些核心线程不会被回收,这意味着它能够更加快速地响应外界的请求

    • CachedThreadPool

      通过Executors的newCachedThreadPool方法来创建.它是一种线程数量不定的线程池,它只有非核心线程,并且其最大线程数为Integer.MAX_VALUE.由于Integer.MAX_VALUE是一个很大的数,实际上就相当于最大线程数可以任意大.当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理新任务,否则就会利用空闲的线程来处理新任务.线程池中的空闲线程都有超时机制,这个超时时长为60秒,超过60秒闲置线程就会被回收.

    • ScheduledThreadPool

      通过Executors的newScheduledThreadPool方法来创建.它的核心线程数量是固定的,而非核心线程数是没有限制的,并且当非核心线程闲置时会被立即回收.ScheduledThreadPool这类线程池主要用于执行定时任务和具有固定周期的重复任务

    • SingleThreadExecutor

      通过Executors的newSingleThreadExecutor方法来创建.这类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行.SingleThreadExecutor的意义在于统一所有的外界任务到一个线程中,这使得在这些任务之间不需要处理线程同步的问题.

    但是…

    阿里巴巴不推荐使用Executors创建线程

    插图

    而是使用原始的ThreadPoolExecutor,这样会让人了解线程的规则.所以关于ThreadPoolExecutor的介绍就很重要了

    ??正文结束??
    展开全文
  • Java线程池核心线程数与最大线程数的区别

    万次阅读 多人点赞 2020-06-23 15:20:45
    第一步: 先判断线程池中当前线程数量是否达到了corePoolSize,若未达到,则新建线程运行此任务,且任务结束后将该线程保留在线程池中,不做销毁处理,若当前线程数量已达到corePoolSize,则进入下一步; 第二步: ...

    线程池策略

    corePoolSize:核心线程数;maximunPoolSize:最大线程数
    每当有新的任务到线程池时,
    第一步: 先判断线程池中当前线程数量是否达到了corePoolSize,若未达到,则新建线程运行此任务,且任务结束后将该线程保留在线程池中,不做销毁处理,若当前线程数量已达到corePoolSize,则进入下一步;
    第二步: 判断工作队列(workQueue)是否已满,未满则将新的任务提交到工作队列中,满了则进入下一步;
    第三步: 判断线程池中的线程数量是否达到了maxumunPoolSize,如果未达到,则新建一个工作线程来执行这个任务,如果达到了则使用饱和策略来处理这个任务。注意: 在线程池中的线程数量超过corePoolSize时,每当有线程的空闲时间超过了keepAliveTime,这个线程就会被终止。直到线程池中线程的数量不大于corePoolSize为止。
    (由第三步可知,在一般情况下,Java线程池中会长期保持corePoolSize个线程。)

    饱和策略

    当工作队列满且线程个数达到maximunPoolSize后所采取的策略
    AbortPolicy:默认策略;新任务提交时直接抛出未检查的异常RejectedExecutionException,该异常可由调用者捕获。
    CallerRunsPolicy:既不抛弃任务也不抛出异常,使用调用者所在线程运行新的任务。
    DiscardPolicy:丢弃新的任务,且不抛出异常。
    DiscardOldestPolicy:调用poll方法丢弃工作队列队头的任务,然后尝试提交新任务
    自定义策略:根据用户需要定制。

    展开全文
  • 每当有新的任务到线程池时,第一步: 先判断线程池中当前线程数量是否达到了corePoolSize,若未达到,则新建线程运行此任务,且任务结束后将该线程保留在线程池中,不做销毁处理,若当前线程数量已达到corePoolSize...

    线程池策略

    corePoolSize:核心线程数;maximunPoolSize:最大线程数

    每当有新的任务到线程池时,
    第一步: 先判断线程池中当前线程数量是否达到了corePoolSize,若未达到,则新建线程运行此任务,且任务结束后将该线程保留在线程池中,不做销毁处理(如果来了新的任务有空闲的核心线程,则不创建新的线程,使用空闲核心线程执行任务),若当前线程数量已达到corePoolSize,则进入下一步;
    第二步: 判断工作队列(workQueue)是否已满,未满则将新的任务提交到工作队列中,满了则进入下一步;
    第三步: 判断线程池中的线程数量是否达到了maxumunPoolSize,如果未达到,则新建一个工作线程来执行这个任务,如果达到了则使用饱和策略来处理这个任务。注意: 在线程池中的线程数量超过corePoolSize时,每当有线程的空闲时间超过了keepAliveTime,这个线程就会被终止。直到线程池中线程的数量不大于corePoolSize为止。
    (由第三步可知,在一般情况下,Java线程池中会长期保持corePoolSize个线程。)

    饱和策略

    当工作队列满且线程个数达到maximunPoolSize后所采取的策略
    AbortPolicy:默认策略;新任务提交时直接抛出未检查的异常RejectedExecutionException,该异常可由调用者捕获。
    CallerRunsPolicy:既不抛弃任务也不抛出异常,使用调用者所在线程运行新的任务。
    DiscardPolicy:丢弃新的任务,且不抛出异常。
    DiscardOldestPolicy:调用poll方法丢弃工作队列队头的任务,然后尝试提交新任务
    自定义策略:根据用户需要定制。

    到此Java 线程池核心线程数与最大线程数的区别介绍完成。

    展开全文
  • 线程池线程频繁出现未捕获的异常,那线程的复用率就大大降低了,需要不断地创建新线程。 做个实验: public class ThreadExecutor { private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor...
  • Java自定义线程池线程总数控制

    千次阅读 2019-04-22 14:53:57
    Java自定义线程池线程总数控制
  • 1. 线程池的概念: 线程池就是首先创建一些线程,... 2.1 在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给...
  • Java并发编程之使用线程池管理线程
  • 线程面试题和答案:线程锁+线程池+线程同步1、并发编程三要素?2、多线程的价值?3、创建线程的有哪些方式?区别是什么?4、创建线程的三种方式的对比?4、线程的生命周期及五种基本状态及转换条件1、Java线程具有...
  • 示例代码: public static void main(String[] args) throws Exception { // 自定义线程工厂 ThreadFactoryBuilder threadFactoryBuilder = new ThreadFactoryBuilder(); ThreadFactory threadFactory...
  • 记录 Spring 项目中的线程池执行线程任务(多少时间之后执行)
  • 1、ThreadPoolExecutor作为java.util.concurrent包对外提供基础实现,以内部线程池的形式对外提供管理任务执行,线程调度,线程池管理等等服务; 2、Executors方法提供的线程服务,都是通过参数设置来实现不同的...
  • 第十四天:线程池+线程工具类

    千次阅读 2020-11-06 14:41:44
    1.线程池 1.1 线程状态介绍 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。线程对象在不同的时期有不同的状态。那么Java中的线程存在哪几种状态呢?Java中的线程 状态被定义在...
  • 线程池线程相关类

    2018-11-22 10:31:54
    线程池在系统启动时即创建大量空闲的线程,程序将一个Runnable对象或Callable对象传给线程池线程池会启动一个线程来执行它们的run()或call方法,当方法执行结束后,线程并不会死亡,而是再次返回到线程池成为空闲...
  • 线程池线程调度

    千次阅读 2017-03-27 21:13:47
    1,线程池有一个缓冲队列:ArrayBlockingQueue final ThreadPoolExecutor threadPool = new ThreadPoolExecutor( CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS,new ArrayBlocki
  • 顾名思义就是线程的池子,里面有若干线程,它们的任务就是执行提交线程池的任务,执行完之后不会退出,而是继续等待或执行新任务。 线程池由两部分组成:任务队列和工作线程。 任务队列:保存待执行的任务; ...
  • 线程进阶之线程池 为什么要创建线程池 在不使用线程池的情景当中,每次任务来临了就创建一个线程去处理,任务结束了就撤销线程。这种方式造成了极大的开销。且不易与维护。通过线程池完成了 1:线程的复用。重用...
  • 线程锁+线程池+线程同步等

    千次阅读 2019-04-28 16:20:39
    2)可见性:可见性指多个线程操作一个共享变量时,其中一个线程对变量进行修改后,其他线程可以立即看到修改的结果。 实现可见性的方法: synchronized或者Lock:保证同一个时刻只有一个线程获取锁执行代码,锁...
  • 线程池线程队列分析-优

    千次阅读 2016-08-24 14:00:36
    并且线程池对系统中的线程数量也起到了很好的限制作用。 · 线程池中的线程数量必须仔细的设置,否则冒然增加线程数量只会带来性能的下降。 · 在定制ThreadPoolExecutor时,遵循KISS原则,通常情况下会提供最好...
  • java设置线程池线程的名字

    万次阅读 2018-03-23 15:06:16
    项目中使用ThreadPoolExecutor进行...如果一个系统中用到了多个线程池,就无法区分哪个线程造成的系统问题。所以每次都需要点 Thread Dumps 去查看线程执行的具体的代码与堆栈信息,推测是哪个地方出的问题。 于是
  • 对于java.util.concurrent.Executors所提供的...队列的上限没有限制(默认上限为Integer.MAX_VALUE),不断的提交新的线程,会造成任务在内存中长时间的堆积。 我们有可能面临如下的场景,主线程不断...
  • //核心线程 int arg2=40;//最大线程数量 int arg3=100;//空余保留时间 // 时间单位 ThreadPoolExecutor pool=new ThreadPoolExecutor(arg1, arg2, arg3,TimeUnit.MILLIS...
  • 一、前言对于从事后端开发的同学来说,线程是必须要使用了,因为使用它可以提升系统的性能。但是,创建线程和销毁线程都是比较耗时的操作,频繁的创建和销毁线程会浪费很多CPU的资源。此外,如果每...
  • 线程一旦start开始到结束,就不能重新start了,就需要重新创建线程,而线程池通过重用已存在的线程,降低线程创建和销毁造成的消耗,并且提供了更强大的功能,比如延时定时线程池 Executor框架 1、Executor 顶层E
  • 线程池线程抛了异常如何处理?

    千次阅读 2021-03-08 22:02:52
    文章目录1. 模拟线程池抛异常2. 如何获取和处理异常方案一...在实际开发中,我们常常会用到线程池,但任务一旦提交线程池之后,如果发生异常之后,怎么处理? 怎么获取到异常信息?在了解这个问题之前,可以先看一下
  • 线程池执行线程任务花费的时间

    千次阅读 2018-05-29 22:36:34
    //线程执行结束时 int l = count.addAndGet( 1 ); if (l == COUNT) { System.out.println(funcname + "spend time:" + (System.currentTimeMillis() - startTime)); } } public static void ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 85,595
精华内容 34,238
关键字:

线程池提交线程