精华内容
下载资源
问答
  • Java线程池七个参数详解

    万次阅读 多人点赞 2019-04-23 11:14:33
    java多线程开发时,常常用到线程池技术,这篇文章是对创建java线程池时的七个参数的详细解释。 从源码中可以看出,线程池的构造函数有7个参数,分别是corePoolSize、maximumPoolSize、keepAliveTime、unit、...

    java多线程开发时,常常用到线程池技术,这篇文章是对创建java线程池时的七个参数的详细解释。

    从源码中可以看出,线程池的构造函数有7个参数,分别是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。下面会对这7个参数一一解释。

    一、corePoolSize 线程池核心线程大小

    线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。

    二、maximumPoolSize 线程池最大线程数量

    一个任务被提交到线程池以后,首先会找有没有空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(后面会介绍)中,如果工作队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。

    三、keepAliveTime 空闲线程存活时间

    一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定

    四、unit 空闲线程存活时间单位

    keepAliveTime的计量单位

    五、workQueue 工作队列

    新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:

    ①ArrayBlockingQueue

    基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。

    ②LinkedBlockingQuene

    基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。

    ③SynchronousQuene

    一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。

    ④PriorityBlockingQueue

    具有优先级的无界阻塞队列,优先级通过参数Comparator实现。

    六、threadFactory 线程工厂

    创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等

    七、handler 拒绝策略

    当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略:

    ①CallerRunsPolicy

    该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。

    ②AbortPolicy

    该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。

    ③DiscardPolicy

    该策略下,直接丢弃任务,什么都不做。

    ④DiscardOldestPolicy

    该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列

     

    到此,构造线程池时的七个参数,就全部介绍完毕了。

    个人独立博客:https://blog.it-follower.com/posts/1035400434.html

    展开全文
  • 给女朋友讲 : Java线程池的内部原理

    万次阅读 多人点赞 2019-11-03 15:09:57
    餐盘在灯光的照耀下格外晶莹洁白,女朋友拿起红酒杯轻轻地抿了一小口,对我说:“经常听你说线程池,到底线程池到底是个什么原理?”

    文章持续更新,微信搜索「 万猫学社 」第一时间阅读。
    关注后回复「 电子书 」,免费获取12本Java必读技术书籍。

    餐厅的约会

    餐盘在灯光的照耀下格外晶莹洁白,女朋友拿起红酒杯轻轻地抿了一小口,对我说:“经常听你说线程池,到底线程池到底是个什么原理?”我楞了一下,心里想女朋友今天是怎么了,怎么突然问出这么专业的问题,但做为一个专业人士在女朋友面前也不能露怯啊,想了一下便说:“我先给你讲讲我前同事老王的故事吧!”

    大龄程序员老王

    老王是一个已经北漂十多年的程序员,岁数大了,加班加不过年轻人,升迁也无望,于是拿着手里的一些积蓄,回老家转行创业。他选择了洗浴行业,开一家洗浴中心,没错,一家正规的洗浴中心。之前在北京的时候,喜欢去的澡堂叫“清华池”,他想了想,就给自己的洗浴中心取名为“线程池”。

    线程池洗浴中心

    线程池开业以后,老王发现有顾客想做足疗,于是就招聘了1个足疗技师,多增加了一项业务增加了收入。随着做足疗的顾客增多,为了赚更多钱又招聘了4个足疗技师。
    过了一段时间,洗浴中心的生意越来越好,做足疗的顾客也越来越多。但是,老王发现自己店里的足疗技师已经有5个足疗技师,再招聘就太多了,支付不起再多工资了。足疗技师忙不过来怎么办?老王是个聪明人,马上想到办法:让顾客排队,有哪个足疗技师做完了,空闲出来了,就在队伍里再叫一个顾客继续做。

    忙碌的周末

    一到周末,来洗浴中心的顾客比平时多了几倍,想足疗的顾客排队时间太长,顾客们已经不耐烦了。老王马上做出反应,又紧急从其他洗浴中心招聘了5个足疗技师,为队伍里顾客做足疗,大大减少排队的顾客。
    不过,有时生意太火爆了,紧急招聘的技师也用上了,顾客排队时间也是很长,再来新的顾客,老王只能满脸赔笑地和顾客说:“您下次再来吧,下次给您找个好技师。”,把顾客拒之门外。
    过了周末以后,店里不能养闲人啊,老王就把紧急招聘的技师都辞退了。

    老王的经营之道

    老王的生意越做越红火,很快就要开分店、融资上市、走上人生巅峰。既然这么成功,就让我们来复盘一下他的经营之道吧:

    如果你了解了老王的经营之道,线程池就不难理解了,把顾客替换成任务,把足疗技师替换成线程线程池洗浴中心就是线程池了,线程池的内部原理就是这样的:

    梦醒

    铃铃铃,闹铃把我吵醒,原来是一场梦啊,我哪有什么女朋友?今天上午有一个面试,赶紧起床洗漱完毕,就出发了。在路上回想那个奇怪的梦,要不再复习一下线程池的内部原理吧!
    先看一下ThreadPoolExecutor类的execute方法:

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        //获取clt,clt记录着线程池状态和运行线程数。
        int c = ctl.get();
        //运行线程数小于核心线程数时,创建线程放入线程池中,并且运行当前任务。
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            //创建线程失败,重新获取clt。
            c = ctl.get();
        }
        //线程池是运行状态并且运行线程大于核心线程数时,把任务放入队列中。
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //重新检查线程池不是运行状态时,
            //把任务移除队列,并通过拒绝策略对该任务进行处理。
            if (! isRunning(recheck) && remove(command))
                reject(command);
            //当前运行线程数为0时,创建线程加入线程池中。
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //运行线程大于核心线程数时并且队列已满时,
        //创建线程放入线程池中,并且运行当前任务。
        else if (!addWorker(command, false))
            //运行线程大于最大线程数时,失败则拒绝该任务
            reject(command);
    }
    

    在execute方法中,多次调用的addWorker方法,再看一下这个方法:

    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            //获取clt,clt记录着线程池状态和运行线程数。
            int c = ctl.get();
            //获取线程池的运行状态。
            int rs = runStateOf(c);
    
            //线程池处于关闭状态,或者当前任务为null
            //或者队列不为空,则直接返回失败。
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;
    
            for (;;) {
                //获取线程池中的线程数
                int wc = workerCountOf(c);
                //线程数超过CAPACITY,则返回false;
                //这里的core是addWorker方法的第二个参数,
                //如果为true则根据核心线程数进行比较,
                //如果为false则根据最大线程数进行比较。
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                //尝试增加线程数,如果成功,则跳出第一个for循环
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                //如果增加线程数失败,则重新获取ctl
                c = ctl.get();
                //如果当前的运行状态不等于rs,说明状态已被改变,
                //返回第一个for循环继续执行
                if (runStateOf(c) != rs)
                    continue retry;
            }
        }
    
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            //根据当前任务来创建Worker对象
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    //获得锁以后,重新检查线程池状态
                    int rs = runStateOf(ctl.get());
    
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive())
                            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;
    }
    

    面试

    一个穿着格子衬衫的中年男子坐在我面前,对我说:“您好,我是今天的面试官。”我微笑地回应:“您好。”面试官面无表情地问我:“线程池一定用过吧,能说说线程池的内部原理嘛?”我差点笑出声来,自信满满地说……

    文章持续更新,微信搜索「 万猫学社 」第一时间阅读。
    关注后回复「 电子书 」,免费获取12本Java必读技术书籍。

    展开全文
  • java线程池

    2017-11-14 14:08:10
    这里搜集了一些关于java线程池的好文章java线程池艺术探索深入理解java线程池-ThreadPoolExecutor

    这里搜集了一些关于java线程池的好文章

    java线程池艺术探索

    深入理解java线程池-ThreadPoolExecutor

    展开全文
  • Java线程池

    千次阅读 2016-01-07 17:04:44
    Java线程池

    为什么要用线程池?

    线程的资源是有限的,当处理一组业务的时候,我们需要不断的创建和销毁线程,大多数情况下,我们需要反复的进行大量的创建和销毁工作,这个动作对于服务器而言,也是很浪费的一种情况,这时候我们可以利用线程池来复用这一部分已经创建过的线程资源,避免不断的创建和销毁的动作。

    线程池的原理

    创建好固定数量的线程,吧线程先存下来,有任务提交的时候,把资源放到等待队列中,等待线程池中的任务队列不断的去消费处理这个队列中的任务

    java的线程池原理

    有5个核心的属性:最大线程数量,核心线程数量,等待队列,任务队列,拒绝策略

    它的执行流程是这样的:

    • 工作者workers数量低于核心工作者数corePoolSize时会优先创建一个工作者worker处理job,处理成功则返回。
    • 工作者workers数量高于核心工作者数时会优先把job放入到待处理队列,放入队列成功时处理结束。
    • 步骤2中入队失败会识别工作者数是否还小于最大工作者数maximumPoolsize,小于的话也会新创建一个工作者worker处理job。
    • 执行拒绝策略 *

    java的线程池框架Executor

    Executor里提供了4种类型的线程池:

    newCachedThreadPool

    • 缓存型池子,先查看池中有没有以前建立的线程,如果有,就 reuse.如果没有,就建一个新的线程加入池中
    • 缓存型池子通常用于执行一些生存期很短的异步型任务
      因此在一些面向连接的daemon型SERVER中用得不多。但对于生存期短的异步任务,它是Executor的首选。
    • 能reuse的线程,必须是timeout IDLE内的池中线程,缺省 timeout是60s,超过这个IDLE时长,线程实例将被终止及移出池。
    注意,放入CachedThreadPool的线程不必担心其结束,超过TIMEOUT不活动,其会自动被终止。

    newFixedThreadPool

    • newFixedThreadPool与cacheThreadPool差不多,也是能reuse就用,但不能随时建新的线程
    • 其独特之处:任意时间点,最多只能有固定数目的活动线程存在,此时如果有新的线程要建立,只能放在另外的队列中等待,直到当前的线程中某个线程终止直接被移出池子
    • 和cacheThreadPool不同,FixedThreadPool没有IDLE机制(可能也有,但既然文档没提,肯定非常长,类似依赖上层的TCP或UDP IDLE机制之类的),所以FixedThreadPool多数针对一些很稳定很固定的正规并发线程,多用于服务器
    • 从方法的源代码看,cache池和fixed 池调用的是同一个底层 池,只不过参数不同:fixed池线程数固定,并且是0秒IDLE(无IDLE),cache池线程数支持0-Integer.MAX_VALUE(显然完全没考虑主机的资源承受能力),60秒IDLE

    newScheduledThreadPool

    • 调度型线程池
    • 这个池子里的线程可以按schedule依次delay执行,或周期执行

    SingleThreadExecutor

    • 单例线程,任意时间池中只能有一个线程
    • 用的是和cache池和fixed池相同的底层池,但线程数目是1-1,0秒IDLE(无IDLE)

    线程池调优

    一般来讲对于一个线程池没有固定的合适的参数,只有通过不断的去调整优化参数,找出最适合自己业务的参数才是最好的调优方式,但是通常来讲,线程池的初始化参数设置是有一定的公式可以借鉴的,在开始业务不是足够膨胀的时候,我们可以通过以下的公式来计算出自己的核心参数的设置。

    首先我们要确认业务类型,不同的业务有不同的计算公式:

    • CPU密集型任务配置尽可能少的线程数量:cpu+1
    • IO密集型任务则由于需要等待IO操作,线程并不是一直在执行任务,则配置尽可能多的线程,如2*Ncpu。
    • 混合型的任务,如果可以拆分,则将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐率要高于串行执行的吞吐率,如果这两个任务执行时间相差太大,则没必要进行分解。我们可以通过Runtime.getRuntime().availableProcessors()方法获得当前设备的CPU个数。
    展开全文
  • Java 线程池

    千次阅读 2021-04-22 17:21:03
    线程池java中自带了四种线程池1.1.newFixedThreadPool 固定大小的线程池 例子1.2.newSingleThreadExecutor 单线程化的线程池 例子1.3.newCachedThreadPool 可缓存线程池 例子1.4.定时器的讲解1.5....
  • java 线程池

    2014-01-03 18:02:48
    Reference: 《创建Java线程池》[1],《Java线程:新特征-线程池》[2], ... 《Java线程池学习》[3],《线程池ThreadPoolExecutor使用简介》[4],《Java5中的线程池实例讲解》[5],《ThreadPoolExe
  • Java线程池Java线程池汇总,看这一篇文章就够了 (1)引言1:Java线程池 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图...
  • 这是【从0到1学习Java线程池】系列文章的第 贰 篇,该系列文章总共三篇,介绍了 Java 线程池的使用以及原理,并且最后会实现一个基本的线程池。本篇文章介绍了 Java 线程池的原理。
  • Java线程池使用说明

    2018-02-24 11:12:14
    Java线程池使用说明Java线程池使用说明Java线程池使用说明

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 265,975
精华内容 106,390
关键字:

java线程池

java 订阅