一篇文章搞清楚Java线程池!知识点整理及经典面试题剖析,必看!!!

1、什么是线程池

线程池的基本思想是一种对象池,在程序启动时就开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。

记得点赞收藏加关注哦 ,我这里也准备了很多面试热门知识点和大厂面试题,希望对大家有帮助!有需要的朋友

可以加q群:580763979      备注:简书   免费领取~

2、使用线程池的好处

减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

运用线程池能有效的控制线程最大并发数,可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

对线程进行一些简单的管理,比如:延时执行、定时循环执行的策略等,运用线程池都能进行很好的实现

3、线程池的主要组件

一个线程池包括以下四个基本组成部分:

线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;

工作线程(WorkThread):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;

任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;

任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。

4、线程池参数设置

参数的设置跟系统的负载有直接的关系,下面为系统负载的相关参数:

tasks,每秒需要处理的的任务数(针对系统需求)

threadtasks,每个线程每钞可处理任务数(针对线程本身)

responsetime,系统允许任务最大的响应时间,比如每个任务的响应时间不得超过2秒。

corePoolSize

系统每秒有tasks个任务需要处理理,则每个线程每钞可处理threadtasks个任务。,则需要的线程数为:tasks/threadtasks,即tasks/threadtasks个线程数。

假设系统每秒任务数为100 ~ 1000,每个线程每钞可处理10个任务,则需要100 / 10至1000 / 10,即10 ~ 100个线程。那么corePoolSize应该设置为大于10,具体数字最好根据8020原则,因为系统每秒任务数为100 ~ 1000,即80%情况下系统每秒任务数小于1000 * 20% = 200,则corePoolSize可设置为200 / 10 = 20。

queueCapacity

任务队列的长度要根据核心线程数,以及系统对任务响应时间的要求有关。队列长度可以设置为 所有核心线程每秒处理任务数 * 每个任务响应时间 = 每秒任务总响应时间 ,即(corePoolSize*threadtasks)responsetime: (2010)*2=400,即队列长度可设置为400。

maxPoolSize

当系统负载达到最大值时,核心线程数已无法按时处理完所有任务,这时就需要增加线程。每秒200个任务需要20个线程,那么当每秒达到1000个任务时,则需要(tasks - queueCapacity)/ threadtasks 即(1000-400)/10,即60个线程,可将maxPoolSize设置为60。

队列长度设置过大,会导致任务响应时间过长,切忌以下写法:

LinkedBlockingQueue queue = new LinkedBlockingQueue();


这实际上是将队列长度设置为Integer.MAX_VALUE,将会导致线程数量永远为corePoolSize,再也不会增加,当任务数量陡增时,任务响应时间也将随之陡增。

keepAliveTime

当负载降低时,可减少线程数量,当线程的空闲时间超过keepAliveTime,会自动释放线程资源。默认情况下线程池停止多余的线程并最少会保持corePoolSize个线程。

allowCoreThreadTimeout

默认情况下核心线程不会退出,可通过将该参数设置为true,让核心线程也退出。

一般说来,大家认为线程池的大小经验值应该这样设置:(其中N为CPU的个数)

如果是CPU密集型应用,则线程池大小设置为N+1

如果是IO密集型应用,则线程池大小设置为2N+1

5、线程池的五种状态

线程池的初始化状态是RUNNING,能够接收新任务,以及对已添加的任务进行处理。

线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。 调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。

线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。 调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。

当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。

当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。

当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。 线程池彻底终止,就变成TERMINATED状态。线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

6、关闭线程池

线程池提供两种关闭线程池方法:shutDown()和shutdownNow()

shutDown()

当线程池调用该方法时,线程池的状态则立刻变成SHUTDOWN状态。此时,则不能再往线程池中添加任何任务,否则将会抛出RejectedExecutionException异常。但是,此时线程池不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。

shutdownNow()

根据JDK文档描述,大致意思是:执行该方法,线程池的状态立刻变成STOP状态,并试图停止所有正在执行的线程,不再处理还在池队列中等待的任务,当然,它会返回那些未执行的任务。

它试图终止线程的方法是通过调用Thread.interrupt()方法来实现的,但是大家知道,这种方法的作用有限,如果线程中没有sleep、wait、Condition、定时锁等应用, interrupt()方法是无法中断当前的线程的。所以,ShutdownNow()并不代表线程池就一定立即就能退出,它可能必须要等待所有正在执行的任务都执行完成了才能退出。

7、各种场景下怎么设置线程数

高并发、任务执行时间短的业务怎样使用线程池?

线程池线程数可以设置为CPU核数+1,减少线程上下文的切换

并发不高、任务执行时间长的业务怎样使用线程池?

这个需要判断执行时间是耗在哪个地方

假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以适当加大线程池中的线程数目(2 * CPU核数),让CPU处理更多的业务。

假如是业务时间长集中在计算操作上,也就是CPU密集型任务,和(1)CPU核数+1 一样吧,线程池中的线程数设置得少一些,减少线程上下文的切换

并发高、业务执行时间长的业务怎样使用线程池?

解决这种类型任务的关键不在于线程池而在于整体架构的设计

8、为什么不推荐使用JUC的线程池?

这样的处理方式更加明确线程池的运行规则,规避资源耗尽的风险

newFixedThreadPool和newSingleThreadExecutor

上面两个主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM


newCachedThreadPool和newScheduledThreadPool

上面两个主要问题是最大线程数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

9、问题

非核心线程延迟死亡,如何实现?

通过阻塞队列poll(),让线程阻塞等待一段时间,如果没有取到任务,则线程死亡

线程池为什么能维持线程不释放,随时运行各种任务?

for (;;) {


            try {

                Runnable r = timed ?

                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :

                    workQueue.take();

                if (r != null)

                    return r;

                timedOut = true;

            } catch (InterruptedException retry) {

                timedOut = false;

            }

        }

    }

在死循环中工作队列workQueue会一直去拿任务:

核心线程的会一直卡在 workQueue.take()方法,让线程一直等待,直到获取到任务,然后返回。

非核心线程会 workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) ,如果超时还没有拿到,下一次循环判断compareAndDecrementWorkerCount就会返回null,Worker对象的run()方法循环体的判断为null,任务结束,然后线程被系统回收。

通过阻塞队列take(),让线程一直等待,直到获取到任务

如何释放核心线程?

将allowCoreThreadTimeOut设置为true。可用下面代码实验

{

    // 允许释放核心线程,等待时间为100毫秒

    es.allowCoreThreadTimeOut(true);

    for(......){

        // 向线程池里添加任务,任务内容为打印当前线程池线程数

        Thread.currentThread().sleep(200);

    }

}

线程数会一直为1。 如果allowCoreThreadTimeOut为false,线程数会逐渐达到饱和,然后大家一起阻塞等待。

非核心线程能成为核心线程吗?

线程池不区分核心线程于非核心线程,只是根据当前线程池容量状态做不同的处理来进行调整,因此看起来像是有核心线程于非核心线程,实际上是满足线程池期望达到的并发状态。

Runnable在线程池里如何执行?

线程执行Worker,Worker不断从阻塞队列里获取任务来执行。。

总结

我这里准备了一线大厂面试资料和超多超硬核的PDF技术文档,以及我为大家精心准备的多套简历模板(不断更新中),希望大家都能找到心仪的工作!

有需要的朋友可以加q群:580763979      备注:简书   免费领取~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容