Java 线程池理解汇总

来源:互联网 发布:汉语大卫知乎 编辑:程序博客网 时间:2024/06/05 19:45

Java中用到线程的情况一般都是使用线程池,省去了创建销毁线程的资源浪费,也可以统一管理线程,关于线程池还是有必要深入了解一下的

线程池了解还是从三个方面入手
一、线程池的优缺点
优点:减少系统创建销毁线程对资源的浪费,对线程统一管理,可以有效的控制最大并发数,提高系统资源的使用率,同时避免过多的资源竞争,避免阻塞。
缺点:使用不当有可能造成过多的创建线程造成死机。
二、线程池的使用以及理解
Java中Executors提供了四种常用的线程池,也可以通过ThreadPoolExecutor创建自定义线程池。
关于Executors提供的四种常用线程池如下:
1.newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无线程可用,则创建心线程,可以看到最大线程数是int最大值,适用于短期异步任务,但是并发量不大,或者负载较轻的服务器。
测试demo
例子
通过修改是否休眠可以验证线程池是动态创建修改的
2.newFixedThreadPool
创建一个定长的线程池,可控制最大并发数,超出的线程任务会在队列中等待,适用于需要限制最大并发数的场景
测试demo
例子
发现不管任务多少,只有两个线程在处理任务。
3.newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定书序执行,如果出现异常,会再新建一个线程继续执行,使用与有顺序要求的场景。
例子
运行可以发现,如果出现异常,会创建新的线程继续执行接下来的任务
4.newScheduledThreadPool
创建一个定长的线程池,支持定时及周期性任务执行
测试例子:
例子
延迟1秒执行,每两秒执行一次
除了ScheduledThreadPool其他的都是返回的ExecutorService,ExecutorService参数最全面的构造方法如下:
这里写图片描述
可以通过ExecutorService自定义自己需要的线程池,所以更需要了解各个参数的意义。
corePoolSize(核心线程数)
当提交一个新任务到线程池,线程池会创建一个新的线程来执行任务,无论其他基本线程是否是空闲状态,这种情况会持续到线程数量达到核心线程数,如果有空闲线程,则不会再新建新的线程
maximumPoolSize(最大线程数)
线程池允许创建的最大线程数,如果缓存队列满了,并且线程数小于最大线程数,这个线程池就会创建新的线程,一直达到最大线程数,如果还有任务,则根据一定策略处理。
keepAliveTime(线程活跃时间)
线程空闲之后存活的时间
unit(时间单位,针对keepAliveTime)
线程活跃时间的单位
workQueue(任务缓存队列)
当任务线程数达到核心线程数时,如果有多余的任务就放在缓存队列中,当缓存队列满了再创建小于最大线程数的线程,关于缓存队列可以选择以下几种:
1.ArrayBlockingQueue:基于数组的阻塞队列,安装FIFO(先进先出)的原则进行排序
2.LinkBlockingQueue:基于链表的阻塞队列,按照先进先出的原则进行排序,吞吐量高于ArrayBlockingQueue
3.SynchronousQueue:一个不存储元素的阻塞队列,每一个插入操作必须等到另外一个线程调用移除操作,否则插入操作一直处于阻塞状态
4.PriorityBlockingQueue:一个具有优先级的无限阻塞队列
threadFactory(线程工程)
用于设置创建新的线程的工程,一般用不到
handler(饱和策略)
这本身是java的一个接口,当队列和线程池都满了,需要一种策略处理新的任务,Java中提供了四种内置的实现类:
1.AbortPolicy 直接抛出异常
2.CallerRunPolicy 只用调用者所在的线程来运行任务
3.DiscardOldestPolicy 丢弃队列里面最近的一个任务,并执行当前的任务
4.DiscardPolicy 不处理,直接丢弃
5.实现RejectedExecutionHandler接口,自定义策略
三、线程池相关面试题(持续更新)
1. 如何避免死锁?
Java多线程中的死锁
死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。这是一个严重的问题,因为死锁会让你的程序挂起无法完成任务,死锁的发生必须满足以下四个条件:

互斥条件:一个资源每次只能被一个进程使用。
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
避免死锁最简单的方法就是阻止循环等待条件,将系统中所有的资源设置标志位、排序,规定所有的进程申请资源必须以一定的顺序(升序或降序)做操作来避免死锁。
2.Java线程池中submit() 和 execute()方法有什么区别?
两个方法都可以向线程池提交任务,execute()方法的返回类型是void,它定义在Executor接口中, 而submit()方法可以返回持有计算结果的Future对象,它定义在ExecutorService接口中,它扩展了Executor接口,其它线程池类像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有这些方法。