范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

面试被问到线程池是怎样实现线程复用的,如何回答?

  如果你只知道线程池的几个参数,面试被问到再深一点的内容就回答不上来了,那么本系列文章带你熟悉线程池源码,更深入的了解线程池。
  ThreadPoolExecutor将分几篇文章做解析,本文是第二篇,第一篇请见Java线程池只知道几个参数?来看看源码你就清楚了(一),关注一波,不错过后续内容,下面是核心知识点。本篇文章讲解Worker类、runWorker()、getTask()
  一、Worker类详解
  1.Worker类图
  Worker是ThreadPoolExecutor的内部类,根据类图可以看出Worker继承了
  AbstractQueuedSynchronizer类,实现了Runnable接口,也就是说,他本身就相当于一个同步队列,结合他的成员变量 thread 和 firstTask,可以知道他实际上就是我们线程池中所说的"线程"。除了父类 AQS 本身提供的独占锁以外,Worker 还提供了一些检查任务线程运行状态以及中断线程相关的方法。
  此外,线程池中还有一个工作队列 workers,用于保存当前全部的 Worker:private final HashSet workers = new HashSet();
  2.Worker类源码
  本质上,Worker类既是一个同步组件,也是一个执行任务的线程。 所以如果有人问你线程池的线程是怎么包装的,至少我们现在知道有个Worker来包装而不是简单的Thread。下面分析下Worker源码:private final class Worker         extends AbstractQueuedSynchronizer         implements Runnable     {         // 工作线程         final Thread thread;              // 初始任务,只在worker第一次执行任务的时候执行,之后都是从workQueue中获取任务执行         Runnable firstTask;              // 线程执行过的任务数         volatile long completedTasks;         //Worker类的构造方法,初始化任务并调用线程工厂创建执行任务的线程         Worker(Runnable firstTask) {             setState(-1);// 调用runWorker()前禁止中断             this.firstTask = firstTask;             this.thread = getThreadFactory().newThread(this);         }         //重写Runnable接口的run()方法         public void run() {           //调用ThreadPoolExecutor类的runWorker(Worker)方法             runWorker(this);         }     	... ...     }
  在Worker类的构造方法中,首先将同步状态state设置为-1,为了防止runWorker方法运行之前被中断。这是因为如果其他线程调用线程池的shutdownNow()方法时,如果Worker类中的state状态的值大于0,则会中断线程,如果state状态的值为-1,则不会中断线程。
  Worker实现了Runable接口,在调用start()方法后,实际执行的是run方法,里面又调用了 ThreadPoolExecutor的runWorker()方法。下面我们来看看这个runWorker方法,知道线程池的线程是如何运行的。二、runWorker()方法分析final void runWorker(Worker w) {     Thread wt = Thread.currentThread();     Runnable task = w.firstTask;     w.firstTask = null;     w.unlock(); // 新创建Worker时默认state为-1,AQS的unlock方法会将其改为0,此后允许使用interruptIfStarted()方法进行中断          // 完成任务以后是否需要移除当前Worker,即当前任务是否意外退出     boolean completedAbruptly = true;          try {         // 循环获取任务,后面解析getTask方法         while (task != null || (task = getTask()) != null) {             // 加锁,防止 shundown 时中断正在运行的任务             w.lock();             // 如果线程池状态为 STOP 或更后面的状态,中断线程任务             if ((runStateAtLeast(ctl.get(), STOP) ||                  (Thread.interrupted() &&                   runStateAtLeast(ctl.get(), STOP))) &&                 !wt.isInterrupted())                 wt.interrupt();             try {                 // 钩子方法,默认空实现,可以自己重写                 beforeExecute(wt, task);                 Throwable thrown = null;                 try {                     // 执行任务                     task.run();                 } catch (RuntimeException x) {                     thrown = x; throw x;                 } catch (Error x) {                     thrown = x; throw x;                 } catch (Throwable x) {                     thrown = x; throw new Error(x);                 } finally {                     // 钩子方法                     afterExecute(task, thrown);                 }             } finally {                 task = null;                 // 任务执行完毕,完成数量+1                 w.completedTasks++;                 w.unlock();             }         }                  completedAbruptly = false;     } finally {         // 根据completedAbruptly决定是否要移除意外退出的Worker,并补充新的Worker         // 也就是说,如果上述过程顺利完成,工作线程没有挂掉,就不删除,下次继续用,        // 否则就在workers中remove掉,然后再调用addWorker()方法添加。       //另外如果获取不到任务,getTask返回空,也会清楚该worker,起到回收非核心线程的目的         processWorkerExit(w, completedAbruptly);     } }
  概括一下runWorker方法的核心逻辑:
  循环调用getTask方法,获取要执行的线程,加锁然后执行任务,如果执行完任务流程,并且没有发生异常导致 Worker 挂掉,就直接复用 Worker(在获取任务的方法 getTask()中循环等待任务),如果执行完任务流程后发现发生异常导致 Worker 挂掉,就从工作队列中移除当前 Worker,并且补充一个新的;
  当然getTask方法就是从队列中获取的,来看下:private Runnable getTask() {     boolean timedOut = false; // Did the last poll() time out?      for (;;) {         int c = ctl.get();         int rs = runStateOf(c);          // Check if queue empty only if necessary.         // 如果线程池关闭了,且队列里的任务都完成了,或者线程池进入了比 STOP更大的状态        //,就不表示没有新任务         if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {             decrementWorkerCount();             return null;         }          // 获取当前工作线程数         int wc = workerCountOf(c);          // 时候设置了核心线程超时(默认false)或当前线程数大于核心线程数,及存在非核心线程,        // 即判断当前当前是否需要进行超时控制         boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;         //如果线程池中的线程数量大于corePoolSize 				//获取大于corePoolSize或者是否正在等待执行任务并且轮询超时 				//并且当前线程池中的线程数量大于1或者任务队列为空         if ((wc > maximumPoolSize || (timed && timedOut))             && (wc > 1 || workQueue.isEmpty())) {             //成功减少线程池中的工作线程数量             if (compareAndDecrementWorkerCount(c))                 return null;             continue;         }          try {             // 阻塞获取任务             Runnable r = timed ?                 // 阻塞 keepaliveTime 以获取任务,如果在 keepaliveTime 时间内没有获取到任务,则返回 null.                 workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :                 workQueue.take();             if (r != null)                 return r;             // 如果获取不到任务,说明非核心线程超时了,下一轮判断确认是否退出循环。            //退出循环后,runWorker方法执行完毕,会被processWorkerExit回收             timedOut = true;         } catch (InterruptedException retry) {             timedOut = false;         }     } }
  概括一下getTask方法:
  判断线程池是否关闭,工作队列是否为空,如果是说明没任务了,直接返回null,否则接着往下判断,判断当前是否存在非核心线程,如果是说明需要进行超时处理,获取任务,如果不需要超时处理,则直接从任务队列获取任务,否则根据 keepaliveTime 阻塞一段时间后获取任务,如果获取不到,说明非核心线程超时,返回 null 交给 runWorker()中的processWorkerExit()方法去删除。
  换句话说,runWorker()方法一旦执行完毕(非核心线程),必然会删除当前的 Worker,而通过 getTask()拿任务的 Worker(核心线程),在线程池正常运行的状态下,核心线程只会一直在 for 循环中等待直到拿到任务,而非核心线程超时以后拿不到任务就会返回一个 null,然后回到 runWorker()中走完processWorkerExit()方法被删除。三、Worker类总结
  看了runWorker和getTask方法后,不知道你对这个Worker有没有进一步理解,那么这个Worker充当了什么作用呢,总结一下:
  1.封装线程,将线程进行包装,与线程池的状态做关联。
  2.通过worker达到线程复用的目的,while循环从队列中获取任务,达到核心线程一直运行,非核心线程运行有过期时间的目的,并且worker数量具有自我恢复能力(其实是重新创建worker),保证线程数量。
  这篇就到这,后续文章会继续分析线程池的其他内容。
  如果你觉得此文对你有一丁点帮助,点个赞,关个注,不失联, 还希望补充什么内容,大家在评论区留言,期待后续精彩内容。
  微信搜索关注订阅号:马老司 ,学习更多技术点

Web3日报0203Web3日报0203推特将不再免费支持访问其APIDamus登上美区AppStore免费社交App前十已从中国大陆下架将为解决垃圾邮件攻击上线关键字过滤功能正通过比特币闪电网络随机阿里高工内产的SpringBoot实战派手册仅发布一天霸榜Github前言近年来,SpringBoot是整个Java社区中最有影响力的项目之一,常常被人看作是JavaEE(JavaPlatformEnterpriseEdition)开发的颠覆者,它将入门即顶配的这款新能源汽车,开着兜风风都是香的观望了好久,终于等到你了!好激动!因为它满足了我对新能源汽车的所有幻想!2月3日,极氪正式官宣第三款车型命名ZEEKRX,同时发布整车外观官图车身尺寸。极氪X是一款紧凑型纯电SUV丢掉造车新势力销冠宝座,G9上市闹出乌龙,何小鹏无法回答小鹏汽车是谁,空降的王凤英能行吗?快消八谈小鹏汽车王凤英何小鹏造车新势力1月交付5218辆同比下降60环比下降54。6出任小鹏汽车总裁后的首月销量数据摆在王凤英面前。熬过壬寅虎年,小鹏汽车疲惫依旧。去年,小鹏汽车掌切断所有供应?美国突然传出消息,任正非放弃幻想,先活下来如今距离美国实施芯片禁令已经两年有余,华为的发展虽然一路坎坷,手机业务方面甚至被迫卖掉荣耀等子品牌断臂求生,但总体来看,结果依然是乐观的。在5G通信方面,即便老美仍在持续造谣华为设新版5澳元纸币将迎大改!澳联储弃用查尔斯三世肖像据澳洲新闻集团2月2日报道称,澳洲央行做出了惊人决定,将在5澳元纸币上弃用查尔斯三世国王的肖像。澳洲央行在一份声明中表示,是时候更新纸币了,以新的设计来尊重澳洲原住民的文化和历史。宋清辉保险行业面临全新的市场环境保险投资收益有望得以改善著名经济学家宋清辉预测,当前,稳增长政策不断权益市场以及房地产回暖,保险行业投资收益有望得以快速改善。在此背景下,新的一年,从预期来看,保险公司在投资方面面临全新的环境,预期投资收沣东新城智造强区快速崛起春节假期后,人民网陕西频道专题报道陕西高质量发展,镜头关注到沣东新城的同力重工西坡基地总装车间。在这里,每12分钟就有一辆非公路用车顺利生产下线,源源不断送往全球各地。其生产线满负市场震荡,拿住莫慌春节之后,A股走势一言难尽节后前两天,投资者期待的开门红全面大涨并未出现,相反,指数一度全面下跌当投资者心生疑虑时,似乎又出现转机,但指数层面也仅是小幅上涨。近期市场震荡加剧,表明送不停!人民日报微信万元现金红包又来人民日报微信祝您新春快乐福运绵绵从除夕起,到元宵节人民日报微信公众号人民日报客户端人民日报视频客户端视界将送出多轮万元现金红包陪您一起过大年进入支付宝红包页面输入红包口令就有可能抢VisualStudioCode1。75发布出品开源中国VSCode1。75已发布,此版本主要带来如下优化配置文件创建和共享配置文件,以配置扩展设置快捷方式等。VSMarketplace签名已发布的扩展现在默认进行代码签名。
赵心童咋了?丁俊晖曾公开质疑其实力水平,球迷丁主任看人太准近日,斯诺克英格兰公开赛吸引了不少粉丝的关注,在本次比赛中,丁俊晖和赵心童都发挥得不是很好,丁俊晖在31领先的情况下,被对手艾伦逆转取胜,令球迷们感到遗憾,而赵心童的表现更令人失望办公室没人了工作怎么正常运行?试试蒲公英X5升级版的路由器!蒲公英X5升级版品牌其实提到蒲公英产品,经常使用电脑的朋友应该不陌生,我第一次接触这个产品的另一个支线产品向日葵而蒲公英的母公司则是海贝锐信息科技股份有限公司(简称贝锐科技),成立93岁老人的生活智慧读书,是世上第一等好事有人问,在这个浮躁的时代,我们为什么还要读书?回答众说纷纭,其中一个回答是这样的读书与不读书,差的是一整个人生。不读书的人犹如井底之蛙,他们的认知范围局限在自己的过往经验和固有思维情感散文,其实一个人,一辈子很长,一辈子很短悄然打开,好像只是为了给自己一个美丽的笑脸,这是心与心的距离。这世间,这一种情,这何堪,这一种刻骨的伤。今天,我还扯着电话的手机,不知他的身影还要继续上演,这一刻,他已经被抛弃,心圣诞节洋美我对圣诞节的记忆,是从一群拥挤的人群中,快乐地抢到一块糖果开始的。记得那年我刚上小学,放学回家时,总是经过一座宏伟壮观的教堂。白墙肃静地围着红砖装扮着挺立的教堂,教堂上高高地耸职场不相信眼泪,要哭回家哭头条创作挑战赛你连自己都不讨好,还指望别人来尊重你!醒醒吧!早点洗洗睡吧!无底线的付出,换来的是别人理所应当小小是我工作时认识的实习生,性格单纯活泼,对待身边所有人都很好。可我私底再给纠缠你的人一次机会吧有一句广为流传的俗语便宜没好货,好货不便宜,意思是好的商品价钱不会便宜,价钱便宜的商品必定不好。在人与人的交往中,太过主动同样会显得很廉价,他却往往无法按捺内心的冲动,一次次厚着脸男女间暧昧关系断联后,大多数人最真实的四个想法暧昧是什么?暧昧就是明明很刻意,但却偏偏要假装不经意明明很开心,却偏要假装不搭理明明很想要,却偏要假装开玩笑一个不去说清楚,一个装着不明白。暧昧这东西,你懂我的图谋不轨,我知你的故120小时不关机实测极米H5提升到底多大,同价位天花板?我现在自己用的是极米H3S,极米H3S是再用完H5之后只能安慰自己说早买早享受,H3S在同价位已经是非常好的选择了,没想到H5升级这么多。极米H5是极米第一款采用CCB标准的投影仪一加11R详细配置泄露,一加10价比百元机创感人纪录作为非旗舰机型,一加11R宣布将采用之前的旗舰芯片高通骁龙8,搭载LPDDR5内存和UFS3。1闪存。虽然高通发布了最新一代的高通8Gen2,但高通8在目前的手机市场上依然十分火爆如何实现100家快递单轨迹推送?做电商的朋友们是不是会遇到以下的一些头疼的问题?由于天气大促爆仓等多种原因造成快递不能及时送达,也未及时通知客户而导致的客户流失及物流成本增加物流状态一直未更新,无法查询物流状态而