精华内容
下载资源
问答
  • 阿里的java代码规范有一条是强制建议开发者不要使用Executors创建线程池。 而是建议我们使用原生的ThreadPoolExecutor进行创建,既然这么建议了,自然是有它建议的道理,下面我们就来简单的分析一下为什么不推荐...

    1 介绍

    阿里的java代码规范有一条是强制建议开发者不要使用Executors创建线程池。
    在这里插入图片描述
    而是建议我们使用原生的ThreadPoolExecutor进行创建,既然这么建议了,自然是有它建议的道理,下面我们就来简单的分析一下为什么不推荐我们使用Executors进行创建。

    2 分析

    作为一个java开发,线程池是我们工作当中,必会去使用的一个工具,关于线程池的好处,自然是不用多说,反正就是好处很多。

    既然是阿里推荐我们使用ThreadPoolExecutor(这个类也就是线程池的核心类) 进行创建线程池,那我们就先看一下这个类的核心方法:
    在这里插入图片描述
    ThreadPoolExecutor 类所提供的几个开放的公共方法就是上图的1,2,3 部分,关于第二部分的方法,主要就是对于已经创建的线程池对象进行功能增强,比如设置核心线程是否可以超时和终止的策略,关于第三部分的公共方法就是获取对应线程池的一些基础参数

    我们重点看一下1部分中的方法,我们会发现,1部分都是关于ThreadPoolExecutor的构造函数,只是对应的参数个数不同而已,我们重点看一下构造函数中参数最多的那个
    在这里插入图片描述
    其实其他的构造函数只是针对没有的参数进行默认初始化,最终都是会调到这个最长的构造函数进行线程池的创建。

    corePoolSize:表示线程池中常驻的核心线程数,等于0表示任务执行完毕之后,没有任何任务请求后销毁线程池的线程,如果大于0即使本地任务执行完毕,核心线程池也不会销毁。
    maximumPoolSize:表示线程池能够容纳的最大线程数,当maximumPoolSize == corePoolSize 表示固定大小的线程池
    keepAliveTime:这个参数是针对于当空闲的时间达到keepAliveTime时,大于corePoolSize 那一部分的线程会被销毁
    unit: 针对参数keepAliveTime的时间单元
    workQueue:缓存队列,用于存放当任务无法被核心线程执行时,会存放到缓存队列中
    threadFactory:线程工厂,可以使用该方法创建对应的线程
    handler:拒绝策略:当缓存队列无法存放新的任务时,并且当前线程池的最大线程数达到maximumPoolSize时,会进行策略淘汰,

    RejectedExecutionHandler:java线程池中主要有以下几个拒绝策略,当然你也可以自定义淘汰策略:

    • AbortPolicy:该策略是线程池的默认淘汰策略,使用该策略时,如果线程池队列满了,丢弃这个任务并抛出RejectExecutionException异常
    • DiscardPolicy:如果线程池满了,会直接丢弃这个任务并不会抛出异常
    • DiscardOldestPolicy:丢弃最老的,也就是说如果队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列。因为队列是队尾进,队头出,所以队头元素是最老的,因此每次都是移除对头元素后再尝试入
    • CallerRunsPolicy:如果添加到线程池失败,那么主线程自己会执行该任务,不会等待线程池中的线程去执行

    线程池的工作流程:

    • 如果正在运行的线程数小于coreSize,马上创建线程执行任务,不排队等待,
    • 如果正在运行的线程数>=coreSize,把该task放入队列
    • 如果队列已满 && 正在运行的线程数 < maximumPoolSize,创建新的线程执行该task;
    • 如果队列已满 && 正在运行的线程数 >= maximumPoolSize,线程池调用handler的reject方法拒绝本次提交。
    • 如果一个提交一个任务,当线程池中线程数<coreSize,即使是其他空闲线程能够执行该任务,也会创建该线程

    理清了线程池的核心流程和核心代码之后,我们再看一下Executors提供给我们的一些快捷创建线程的方法
    在这里插入图片描述

    主要的几个方法是:

    public static ExecutorService newCachedThreadPool()
    public static ExecutorService newFixedThreadPool(int nThreads)
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
    public static ExecutorService newSingleThreadExecutor()
    

    针对于**newCachedThreadPool()**创建线程的方法
    在这里插入图片描述
    他的内部实际上是设置核心线程数为0,并且对应的最大线程数为Integer.MAX_VALUE,设置60秒线程空闲销毁策略,并且设置了一个同步队列SynchronousQueue。
    SynchronousQueue没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素。
    可能会创建大量的线程,最终会导致OOM

    针对newFixedThreadPool(int nThreads) 创建线程池的方法:
    在这里插入图片描述
    他的内部是设置了一个固定nThreads个数的核心线程池,创建了容量为Integer.MAX_VALUE大小的LinkedBlockingQueue的缓存队列,
    可能会导致当任务无法被核心线程执行时,会无限的存储到缓存队列中,并且没有对应的淘汰策略,最终会导致OOM

    针对**newScheduledThreadPool(int corePoolSize)**创建线程池的方法:
    在这里插入图片描述
    内部的实现逻辑和newCachedThreadPool方法差不多,只是他们的核心线程可以被设置一个固定的值,最大的线程数仍然是Integer.MAX_VALUE
    也可能会 导致创建大量的线程池,出现OOM的问题

    正对newSingleThreadExecutor() 的创建线程池的方法:
    在这里插入图片描述
    它的内部实现逻辑和newFixedThreadPool基本一致,使用了Integer.MAX_VALUE最大容量的缓存队列,
    最终可能导致新加入的任务会一直存储到缓存队列中,没有淘汰策略,导致OOM

    展开全文
  • 线程池:初始化好实例后,把任务放进去,再调度执行 new Thread 的弊端: 1、每次new Thread 新建对象 性能差 2、线程缺乏统一管理,可能无限制的创建线程,相互竞争,有可能占有过多系统资源导致死机或OOM 3、缺少...

    线程池:初始化好实例后,把任务放进去,再调度执行

    new Thread 的弊端:
    1、每次new Thread 新建对象  性能差
    2、线程缺乏统一管理,可能无限制的创建线程,相互竞争,有可能占有过多系统资源导致死机或OOM
    3、缺少更多的功能,如果多执行、定期执行、线程中断
    
    线程池的好处:
    1、重用存在的线程,减少对象的创建、消亡的开销,性能好
    2、可有效的控制最大并发线程数,提高系统资源利用率,同时可以避免过多竞争资源竞争,避免阻塞
    3、提供定时执行、定期执行、单线程、并发数控制等功能
    

    相关的类
    ThreadPoolExecutor

    参数:
    1.corePoolSize:核心线程数量
    2.workQueue:阻塞队列,存储等待执行的任务,很重要,会对线程池运行过程产生重大影响
    3.maximumPoolsize: 线程最大线程数
    
    如何合理的设置这些数量:

    1、如果想降低系统资源的消耗,包括UPC的使用率,可以设置一个队列的较大容量和较小的线程池容量,这样会降低线程处理任务的吞吐量。
    2、如果我们提交的任务会经常发生阻塞,我们可以考虑设置线程最大数来重新设定线程池最大容量,如果队列容量设置较小,通常需要把线程池的容量设置的大一些,这样CPU使用率会高一些,但是如果线程池的容量设置过大,在提交任务,并发量会增加,所以线程间的调度就是要考虑的问题,这样,反而会将底处理任务的吞吐量。

    KeepAliveTIme:线程没有任务执行时最多保持多久时间终止。
    当线程池中的数量大于corePoolSize,如果没有新的任务提交,线程不会立即销毁,而是等待,直到等待的时间超过了KeepAliveTIme 才会被销毁
    unit:KeepAliveTIme的时间单位
    threadFactory:线程工厂,用来创建线程
    rejectHandler:当拒绝处理任务时的策略

    如果workQueue对应的阻塞队列满了,并且没有多余的线程池,需要采用拒绝策略
    1、直接抛出异常(默认)
    2、用调用者所在的线程执行任务
    3、丢弃队列中最靠前的任务,并执行当前任务
    4、直接丢弃这个任务

    Running:能接受新提交的任务,并且也能处理正在阻塞中的队列中的任务
    shutdown:不能再接受新提交的任务,但是可以继续处理阻塞队列中保存的任务
    stop:不能再接受新提交的任务,不可以继续处理队列中保存的任务
    TIDYING:如果所有任务终止了,
    TERMINATED:

    提供的方法
    基本方法
    execute():提交任务,交给线程池执行
    submit() : 提交任务,能够返回执行结果, execute+Future
    shutdown(): 关闭线程池,等待任务都执行完
    shutdownNow(): 关闭线程池,不等待任务执行完
    监控方法
    getTaskCount():线程池已执行和未执行的任务总数
    getCompletedTaskCount(): 已完成的任务数量
    getPoolSize(): 线程池当前的线程数量
    getActiveCOunt():当前线程池正在执行任务的线程数量
    
    Excutors.newCachThreadPool:
    Excutors.newFixedThreadPool :定长
    Excutors.newScheduledThreadPool
    定长,支持定时,以及周期性的任务执行
    Excutors.newSingleThreadExecutor 单线程的线程池
    
    CPU 密集型任务,就需要尽量压榨CPU,参考值可以设定为NCPU+1
    IO密集时,参考值可以设置为2*NCPU
    
    展开全文
  • 8.4 线程池使用FutureTask时候需要注意的一点事 线程池使用FutureTask的时候如果拒绝策略设置为了 DiscardPolicy和DiscardOldestPolicy并且在被拒绝的任务的Future对象上调用无参get方法那么调用线程会一直被...

    8.4 线程池使用FutureTask时候需要注意的一点事

    线程池使用FutureTask的时候如果拒绝策略设置为了 DiscardPolicyDiscardOldestPolicy并且在被拒绝的任务的Future对象上调用无参get方法那么调用线程会一直被阻塞。

    问题复现

    下面就通过一个简单的例子来复现问题:

    public class FutureTest {
    
        //(1)线程池单个线程,线程池队列元素个数为1
            private final static ThreadPoolExecutor executorService = new ThreadPoolExecutor(1, 1, 1L, TimeUnit.MINUTES,
                new ArrayBlockingQueue<Runnable>(1),new ThreadPoolExecutor.DiscardPolicy());
    
        public static void main(String[] args) throws Exception {
    
            //(2)添加任务one
            Future futureOne = executorService.submit(new Runnable() {
                @Override
                public void run() {
    
                    System.out.println("start runable one");
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            
            //(3)添加任务two
            Future futureTwo = executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("start runable two");
                }
            });
            
            //(4)添加任务three
            Future futureThree=null;
            try {
                futureThree = executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("start runable three");
                    }
                });
            } catch (Exception e) {
                System.out.println(e.getLocalizedMessage());
            }
            
           
            System.out.println("task one " + futureOne.get());//(5)等待任务one执行完毕
            System.out.println("task two " + futureTwo.get());//(6)等待任务two执行完毕
            System.out.println("task three " + (futureThree==null?null:futureThree.get()));// (7)等待任务three执行完毕
    
            
            executorService.shutdown();//(8)关闭线程池,阻塞直到所有任务执行完毕
        }
    

    运行代码结果为:

    image.png
    • 代码(1)创建了一个单线程并且队列元素个数为1的线程池,并且拒绝策略设置为了DiscardPolicy

    • 代码(2)向线程池提交了一个任务one,那么这个任务会使用唯一的一个线程进行执行,任务在打印 start runable one后会阻塞该线程5s.

    • 代码(3)向线程池提交了一个任务two,这时候会把任务two放入到阻塞队列

    • 代码(4)向线程池提交任务three,由于队列已经满了则会触发拒绝策略丢弃任务three,从执行结果看在任务one阻塞的5s内,主线程执行到了代码(5)等待任务one执行完毕,当任务one执行完毕后代码(5)返回,主线程打印出task one null。任务one执行完成后线程池的唯一线程会去队列里面取出任务two并执行所以输出start runable two然后代码(6)会返回,这时候主线程输出task two null,然后执行代码(7)等待任务three执行完毕,从执行结果看代码(7)会一直阻塞不会返回,至此问题产生,如果把拒绝策略修改为DiscardOldestPolicy也会存在有一个任务的get方法一直阻塞只是现在是任务two被阻塞。但是如果拒绝策略设置为默认的AbortPolicy则会正常返回,并且会输出如下结果:

    start runable one
    Task java.util.concurrent.FutureTask@135fbaa4 rejected from java.util.concurrent.ThreadPoolExecutor@45ee12a7[Running, pool size = 1, active threads = 1, queued tasks = 1, completed tasks = 0]
    task one null
    start runable two
    task two null
    task three null
    

    问题分析

    要分析这个问题需要看下线程池的submit方法里面做了什么,submit方法代码如下:

        public Future<?> submit(Runnable task) {
            ...
            //(1)装饰Runnable为Future对象
            RunnableFuture<Void> ftask = newTaskFor(task, null);
            execute(ftask);
            //(6)返回future对象
            return ftask;
        }
        
            protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
            return new FutureTask<T>(runnable, value);
        }
        
    
     public void execute(Runnable command) {
             ...
            //(2) 如果线程个数消息核心线程数则新增处理线程处理
            int c = ctl.get();
            if (workerCountOf(c) < corePoolSize) {
                if (addWorker(command, true))
                    return;
                c = ctl.get();
            }
            //(3)如果当前线程个数已经达到核心线程数则任务放入队列
            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);
            }
            //(4)尝试新增处理线程进行处理
            else if (!addWorker(command, false))
                reject(command);//(5)新增失败则调用拒绝策略
        }
    

    根据代码可以总结如下:

    • 代码(1)装饰Runnable为FutureTask对象,然后调用线程池的execute方法
    • 代码(2) 如果线程个数消息核心线程数则新增处理线程处理
    • 代码(3)如果当前线程个数已经达到核心线程数则任务放入队列
    • 代码(4)尝试新增处理线程进行处理,失败则进行代码(5),否者直接使用新线程处理
    • 代码(5)执行具体拒绝策略。

    所以要分析上面例子中问题所在只需要看步骤(5)对被拒绝任务的影响,这里先看下拒绝策略DiscardPolicy的代码:

        public static class DiscardPolicy implements RejectedExecutionHandler {
            public DiscardPolicy() { }
            public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            }
        }
    

    可知拒绝策略rejectedExecution方法里面什么都没做,所以代码(4)调用submit后会返回一个future对象,这里有必要在重新说future是有状态的,future的状态枚举值如下:

        private static final int NEW          = 0;
        private static final int COMPLETING   = 1;
        private static final int NORMAL       = 2;
        private static final int EXCEPTIONAL  = 3;
        private static final int CANCELLED    = 4;
        private static final int INTERRUPTING = 5;
        private static final int INTERRUPTED  = 6;
    

    在步骤(1)的时候使用newTaskFor方法转换Runnable任务为FutureTask,而FutureTask的构造函数里面设置的状态就是New。

        public FutureTask(Runnable runnable, V result) {
            this.callable = Executors.callable(runnable, result);
            this.state = NEW;       // ensure visibility of callable
        }
    

    所以使用DiscardPolicy策略提交后返回了一个状态为NEW的future对象。
    那么我们下面就需要看下当调用future的无参get方法时候当future变为什么状态时候才会返回那,那就需看下FutureTask的get()方法代码:

        public V get() throws InterruptedException, ExecutionException {
            int s = state;
            //当状态值<=COMPLETING时候需要等待,否者调用report返回
            if (s <= COMPLETING)
                s = awaitDone(false, 0L);
            return report(s);
        }
        
       private V report(int s) throws ExecutionException {
       Object x = outcome;
       //状态值为NORMAL正常返回
       if (s == NORMAL)
           return (V)x;
       //状态值大于等于CANCELLED则抛异常
       if (s >= CANCELLED)
           throw new CancellationException();
       throw new ExecutionException((Throwable)x);
    }
    

    也就是说当future的状态>COMPLETING时候调用get方法才会返回,而明显DiscardPolicy策略在拒绝元素的时候并没有设置该future的状态,后面也没有其他机会可以设置该future的状态,所以future的状态一直是NEW,所以一直不会返回,同理DiscardOldestPolicy策略也是这样的问题,最老的任务被淘汰时候没有设置被淘汰任务对于future的状态。

    那么默认的AbortPolicy策略为啥没问题那?其实AbortPolicy策略时候步骤(5)直接会抛出RejectedExecutionException异常,也就是submit方法并没有返回future对象,这时候futureThree是null。

    所以当使用Future的时候,尽量使用带超时时间的get方法,这样即使使用了DiscardPolicy拒绝策略也不至于一直等待,等待超时时间到了会自动返回的,如果非要使用不带参数的get方法则可以重写DiscardPolicy的拒绝策略在执行策略时候设置该Future的状态大于COMPLETING即可,但是查看FutureTask提供的方法发现只有cancel方法是public的并且可以设置FutureTask的状态大于COMPLETING,重写拒绝策略具体代码可以如下:

    public class MyRejectedExecutionHandler implements RejectedExecutionHandler{
    
        @Override
        public void rejectedExecution(Runnable runable, ThreadPoolExecutor e) {
             if (!e.isShutdown()) {
                 if(null != runable && runable instanceof FutureTask){
                     ((FutureTask) runable).cancel(true);
                 }
             }
        }
    
    }
    

    使用这个策略时候由于从report方法知道在cancel的任务上调用get()方法会抛出异常所以代码(7)需要使用try-catch捕获异常代码(7)修改为如下:

            try{
                System.out.println("task three " + (futureThree==null?null:futureThree.get()));// (6)等待任务three
            }catch(Exception e){
                System.out.println(e.getLocalizedMessage());
            }
    

    执行结果为:


    image.png

    当然这相比正常情况下多了一个异常捕获,其实最好的情况是重写拒绝策略时候设置FutureTask的状态为NORMAL,但是这需要重写FutureTask方法了,因为FutureTask并没有提供接口进行设置。

    总结

    本文通过案例介绍了线程池中使用FutureTask时候当拒绝策略为DiscardPolicyDiscardOldestPolicy的时候在被拒绝的任务的FutureTask对象上调用get()方法会导致调用线程一直阻塞,所以在日常开发中尽量使用带超时参数的get方法以避免线程一直阻塞,另外通过重写这些拒绝策略设置拒绝任务的状态也可以达到想要的效果。多线程下使用时候最好使用ThreadLocal对象。更多并发编程中需要注意的情景以及解决方法敬请期待 Java中高并发编程必备基础之并发包源码剖析 一书出版

    欢迎关注微信公众号 技术原始积累

    展开全文
  • 使用线程池注意事项

    2020-09-22 10:43:14
    1.大厂开发规范,线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 1)FixedThreadPool 和 SingleThreadPool: ...
    • 1.大厂开发规范,线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
    • 1)FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM
    • 2)CachedThreadPool 和 ScheduledThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM
    • 2.使用线程池,还要注意异常处理的问题,例如通过 ThreadPoolExecutor 对象的 execute() 方法提交任务时,如果任务在执行的过程中出现运行时异常,会导致执行任务的线程终止;不过,最致命的是任务虽然异常了,但是你却获取不到任何通知,这会让你误以为任务都执行得很正常。虽然线程池提供了很多用于异常处理的方法,但是最稳妥和简单的方案还是捕获所有异常并按需处理
    展开全文
  • 从业以来其实我一直对线程池接触的比较少,了解的...一、简单来说使用线程池有三个好处: 1、降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 2、提高响应速度:当任务到达时,任务可以不...
  • 线程池扩展 ThreadPoolExecutor beforeExecutor afterExecutor terminate 执行策略 执行任务,不关心结果 void execute(Runnable command) 执行任务,需要对结果进行处理 Future submit...
  • 8.4 线程池使用FutureTask时候需要注意的一点事 线程池使用FutureTask的时候如果拒绝策略设置为了 DiscardPolicy和DiscardOldestPolicy并且在被拒绝的任务的Future对象上调用无参get方法那么调用线程会一直被阻塞。...
  • 线程池使用注意事项

    2011-09-29 09:41:02
    线程池:简单地说,线程池 就是预先创建好一批线程,方便、快速地处理收到的业务。比起传统的到来一个任务,即时创建一个线程来处理,节省了线程的创建和回收的开销,响应更快,效率更高。 线程池的实现: ...
  • 并发编程经历:线程池使用

    万次阅读 多人点赞 2019-07-31 18:38:24
    线程池使用 使用线程池管理线程可以最大程度的利用线程,节省资源消耗,它通过利用已有的线程多次循环执行多个任务从而提高系统的处理能力。 我们可以通过java.util.concurrent.ThreadPoolExecutor类来创建线程池...
  • 主要介绍了JAVA 创建线程池注意事项,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
  • SpringBoot异步接口调用与多线程线程池使用注意事项说明.pdf
  • 线程池中的线程是重复使用的,即一次使用完后,会被重新放回线程池,可被重新分配使用。   因此,ThreadLocal线程变量,如果保存的信息只是针对一次请求的,放回线程池之前需要清空这些Threadlocal变量的值(或者...
  • 使用线程池的目的是希望线程控制在某个范围内,以免造成服务崩溃,所以在使用线程池的时候要多加注意。 记录原因:一个webservice中转项目,需求很简单,提供webservice接口接收数据,然后入库,再通过webservice将...
  • C#线程池使用需要注意的地方

    千次阅读 2018-06-06 16:00:53
    https://blog.csdn.net/u014511737/article/details/47440495
  • 线程池使用FutureTask的时候如果拒绝策略设置为了DiscardPolicy和DiscardOldestPolicy并且在被拒绝的任务的Future对象上调用无参get方法那么调用线程会一直被阻塞。
  • java 什么是线程池及为什么要使用线程池
  • 使用线程池的好处

    2014-05-15 14:58:03
    使用线程池的好处】多线程的环境中,尽量采用线程池  在什么情况下使用线程池?   1.单个任务处理的时间比较短   2.将需处理的任务的数量大   使用线程池的好处:   1.减少在创建和销毁...
  •  1、技术背景:服务器程序利用线程技术响应客户请求已经司空见惯,但是线程的使用是有待优化和处理的。单线程执行并不是一个高效的方式,这个时候可能要考虑高并发,多线程等方式。线程池也是线程优化的一种方式。...
  • java线程池注意事项

    2015-10-24 21:07:00
    java线程池是建立一个技术进步的线程。到来之前线程池打造一批在线程的线程尚未,队列上的备用,然后再利用这些资源。减少频繁创建和销毁对象, 1.jdk1.5以上提供了现成的线程池 2.java线程池的顶级接口是Executor...
  • 1. 为什么需要线程池?  多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。   假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 ...
  • 第一,反复创建线程系统开销比较大,每个线程创建和销毁都需要时间,如果任务比较简单,那么就有可能导致创建和销毁线程消耗的资源比线程执行任务本身消耗的资源还要大。 第二,过多的线程会占用过多的内存等...
  • 有返回值:用户需要Callable、Futrue等来接收最终的处理结果,但这个过程是异步非阻塞的。 无返回值:用户提交请求到接口之后不需要任何返回值,请求到达服务端之后就没有任何关系了,用户可以不等待而去做其他的...
  • 线程池使用详解

    2017-04-27 21:38:31
    使用线程池可以很好的提高性能,尤其是当程序中需要创建大量生存期很短的线程时,更应该考虑使用线程池线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。...
  •  如果使用线程池(如Executor),那么即使即使父线程已经结束,子线程依然存在并被池化。这样,线程池中的线程在下一次请求被执行的时候,ThreadLocal对象的get()方法返回的将不是当前线程中设定的变量,因为池中的...
  • 近期项目中涉及到使用多线程和线程池,但是可能不少人知道怎么用却不知道这样做的好处是什么,了解一下: 1、提高资源利用率 线程池可以重复利用已经创建了的线程 2、提高响应速度 因为当线程池中的线程没有超过...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 147,502
精华内容 59,000
关键字:

使用线程池需要注意的点