重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
这篇文章主要介绍“线程的相关知识点总结”,在日常操作中,相信很多人在线程的相关知识点总结问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”线程的相关知识点总结”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
创新互联建站坚持“要么做到,要么别承诺”的工作理念,服务领域包括:网站设计制作、成都网站设计、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的桑日网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!
先说阻塞队列,即生产者消费者模式
举个列子
在多线程下:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒。
为什么需要BlockingQueue?
好处是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都给你办了。在concurrent包发布以前,在多线程环境下,我们每个程序员都必须去自己控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。
线程池的底层就是阻塞队列:
ArrayBlockingQueue:由数组结构组成的有界阻塞队列。
LinkedBlockingQueue:由链表结构组成的有界(默认值为Integer.MAX_VALUE)阻塞队列。
PriorityBlockingQueue:支持优先级排序的无界塞队列。
DelayQueue:使用优先级队列实现的延迟无界阻塞队列。
SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列。
LinkedTransferQueue:由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:由链表结构组成的双向阻塞队列
说一下SynchronousQueue,与其他BlockingQueue不同,SynchronousQueue是一个不存储元素的BlockingQueue(没有容量)。
每一个put操作必须要等待一个take操作,否则不能继续添加元素,反之亦然。
创建线程的几种方式
1.继承Thread类
2.实现Runable接口
3.实现Callable接口
Callable FutureTask实现了Runable接口,构造方法传入Callable(适配器模式)。面向接口的编程。
4.线程池…..
ThreadPoolExecutor + 阻塞队列
1、newCachedThreadPool:用来创建一个可以无限扩大的线程池,适用于负载较轻的场景,执行短期异步任务。(可以使得任务快速得到执行,因为任务时间执行短,可以很快结束,也不会造成cpu过度切换)
2、newFixedThreadPool:创建一个固定大小的线程池,因为采用无界的阻塞队列,所以实际线程数量永远不会变化,适用于负载较重的场景,对当前线程数量进行限制。(保证线程数可控,不会造成线程过多,导致系统负载更为严重)
3、newSingleThreadExecutor:创建一个单线程的线程池,适用于需要保证顺序执行各个任务。
4、newScheduledThreadPool:适用于执行延时或者周期性任务。
核心参数
1.corePoolSize:线程池中的常驻核心线程数
2.maximumPooISize:线程池能够容纳同时执行的最大线程数,此值必须大于等于1
3.keepAliveTime:多余的空闲线程的存活时间。当前线程池数量超过corepooISize时,当空闲时间达到keepAeTime值时,多余空闲线程会被销毁直到只剩下corepooISize个线程为止
4.unit:keepAliveTime的单位
5.workQueue:任务队列,被提交但尚未被执行的任务
6.threadFactory:表示生成线程池中工作线程的线程工厂,用于创建线程一般用默认的即可
7.handIer:拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数(maximumPooISize)底层原理
1. 在创建了线程池后,等待提交过来的任务请求。
2. 当调用execute()方法添加一个请求任务时,线程池会做如下判断:
2.1如果正在运行的线程数量小于corePoolSize,那么直接创建线程运行这个任务;
2.2如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;
2.3如果这时候队列满了且正在运行的线程数量还小于maximumPoolSze,那么还是要创建非核心线程立刻运行这个任务;
2.4如果队列满了且正在运行的线程数量大于或等于maximumPoolSze,那么线程池会启动饱和拒绝略来执行。
3. 当一个线程完成任务时,它会从队列中取下一个任务来执行。
4. 当一个线程无事可做超过一定的时间(keepAliveTime)时,线程池会判断:|
如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后它最终会收缩到corePoolStze的大小。拒绝策略
AbortPolicy(默认):直抛出RejectedExecutionExcepton异常阻止系统正常运行。
CallerRunsPolicy:“调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退给调用者运行。
DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队中尝试再次提交当前任务。
DiscardPolicy:直接丢弃任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种方案。
扩展:合理配置线程,你是如何考虑的?
1.cpu密集型
CPU密集的意思是该任务需要大量的运算,而没有阻塞,CPU一直全速运行。
CPU密集任务只有在真正的多核CPU上才可能得到加速(通过多线程),而在单核CPU上,无论你开几个模拟的多线程该任务都不可能得到加速,因为CPU总的运算能力就那些。
因为CPU密集型任务使得CPU使用率很高,若开过多的线程数,会造成CPU过度切换,所以CPU密集型任务配置尽可能少的线程数量:
一般公式:CPU核数+1个线程的线程池2.io密集型
可以使用稍大的线程池,一般为2*CPU核心数。 IO密集型任务CPU使用率并不高,因此可以让CPU在等待IO的时候有其他线程去处理别的任务,充分利用CPU时间。
故需要多配置线程数:
参公式:CPU核数/1-阻塞系数 阻塞系数在0.8~0.9之间
比如8核CPU:8/1-0.9=80个线程数
线程池为什么要使用阻塞队列?
1.因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM,并且会造成cpu过度切换。
2.阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进入wait状态,释放cpu资源。当队列中有任务时才唤醒对应线程从队列中取出消息进行执行。使得在线程不至于一直占用cpu资源。
到此,关于“线程的相关知识点总结”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注创新互联网站,小编会继续努力为大家带来更多实用的文章!