线程创建的四种方法以及特点:
1.直接new Thread 一般不使用该方法
缺点:
1)、每次new Thread新建对象性能差。
2)、线程缺乏统一管理,可能无限制新建线程,相互之间竞争及可能占用过多系统资源导致死机或oom。
3)、缺乏更多功能,如定时执行、定期执行、线程中断。
2.实现runnable接口
优点:没有返回值的时候使用特别方便
缺点:没有返回值,也不能抛异常
3.实现Callable和Future接口
Java针对Future接口提供的具体实现类FutureTask
实际调用的是Callable中的call方法,而Callable中没有直接操作runnable的方法,
只能通过Future中的get方法来间接实现对runnable中run方法的调用。
优点:有返回值,可以抛出异常。
4.使用线程池来创建线程 适用于创建多个线程
使用ExecutorService、Callable、Future实现有返回结果的线程,线程池的具体实现实际上是依赖于ThreadPoolExecutor
优点:
1)重用存在的线程,减少对象创建、消亡的开销,性能佳
2)可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞
3)提供定时执行、定期执行、单线程、并发数控制等功能。
缺点:
不管使用与否,线程池都会创建,可能会浪费资源
线程是稀缺资源,如果被无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,合理的
使用线程池对线程进行统一分配、调优和监控,
有以下好处:1、降低资源消耗;2、提高响应速度;3、提高线程的可管理性。
Java1.5中引入的Executor框架把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线
程池,而不用关心该任务是如何执行、被哪个线程执行,以及什么时候执行。
乏、系统频繁进行上下文切换以及内存溢出等问题,因为线程池中的每一个线程可能会轮询地执行多个任务
ExecutorService是Java中对线程池定义的一个接口,它java.util.concurrent包中。Java API
对ExecutorService接口的实现有两个(ThreadPoolExecutor和ScheduledThreadPoolExecutor)
,所以这两个即是Java线程池具体实现类。除此之外,ExecutorService还继承了Executor接口(
注意区分Executor接口和Executors工厂类),这个接口只有一个execute()方法
Executors只是一个工厂类,它所有的方法返回的都是ThreadPoolExecutor、
ScheduledThreadPoolExecutor这两个类的实例
Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一
个执行线程的工具。真正的线程池接口是ExecutorService。
线程池的作用:
为什么要用线程池:
服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)
系统预定义的线程池:
1、 可缓存线程池
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收
空闲线程,若无可回收重用时则新建线程。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
可灵活的往线程池中添加线程。
则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线
程。
很有会造成系统瘫痪。
特点:
2、 定长线程池
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
特点:
3、周期定长线程池
newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行。
特点:
4、单线程化线程池
newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,
保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
特点:
ExecutorService singleThreadPool = Executors.newFixedThreadPool(1);
ThreadPoolExecutor executor = (ThreadPoolExecutor) singleThreadPool;
executor.setCorePoolSize(4); 没问题,可以修改核心线程数
//但是如果Executors.newSingleThreadExecutor修改出错
*
newCachedThreadPool
用来创建一个可以无限扩大的线程池,适用于服务器负载较轻,执行很多短期异步任务。
newFixedThreadPool
创建一个固定大小的线程池,因为采用无界的阻塞队列,所以实际线程数量永远不会变化,适用于可以预测线程数量的业务中,或者服务器负载较重,对当前线程数量进行限制。
newSingleThreadExecutor
创建一个单线程的线程池,适用于需要保证顺序执行各个任务,并且在任意时间点,不会有多个线程是活动的场景。
newScheduledThreadPool
可以延时启动,定时启动的线程池,适用于需要多个后台线程执行周期任务的场景。
newWorkStealingPool
创建一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu数量的线程来并行执行,适用于大耗时的操作,可以并行来执行
綫程池的7大參數 ThreadPoolExecutor
序号 | 名称 | 类型 | 含义 |
---|---|---|---|
1 | corePoolSize | int | 核心线程池大小 |
2 | maximumPoolSize | int | 最大线程池大小 |
3 | keepAliveTime | long | 线程最大空闲时间 |
4 | unit | TimeUnit | 时间单位 |
5 | workQueue | BlockingQueue |
线程等待队列 |
6 | threadFactory | ThreadFactory | 线程创建工厂 |
7 | handler | RejectedExecutionHandler | 拒绝策略 |
1、无界队列:队列大小无限制,常用的为无界的LinkedBlockingQueue,当任务耗时较长时可能会导致大量新任务在队列中堆积最终导致OOM。
2、有界队列:常用的有两类,一类是遵循FIFO原则的队列如ArrayBlockingQueue与有界的LinkedBlockingQueue,
另一类是优先级队列如PriorityBlockingQueue
> PriorityBlockingQueue中的优先级由任务的Comparator决定。
> 使用有界队列时队列大小需和线程池大小互相配合,线程池较小有界队列较大时可减少内存消耗,降低cpu使用率和上下文切换,但是可能会限制系统吞吐量。
> 实际开发中一般应该使用有界队列。
3、同步移交队列:如果不希望任务在队列中等待而是希望将任务直接移交给工作线程,可使用SynchronousQueue
作为等待队列
> SynchronousQueue不是一个真正的队列,而是一种线程之间移交的机制。要将一个元素放入
SynchronousQueue中,必须有另一个线程正在等待接收这个元素。只有在使用无界线程池或者有饱和策略时才建议使用该队列。
1、AbortPolicy直接抛出异常。
2、CallerRunsPolicy只用调用所在的线程运行任务。
3、DiscardOldestPolicy丢弃队列里最近的一个任务,并执行当前任务。
4、DiscardPolicy不处理,丢弃掉。
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor方法,这样的处理方式让编程人员更加明确线程池的运行规则,规避资源耗尽的风险
说明: Executors返回的线程池对象的弊端:
java.util.concurrent.ExecutorService是java线程池框架的主要接口,用Future保存任务的运行状态
及计算结果,主要方法有:
void execute(Runnable)提交任务到线程池
Future submit(Runnable)提交任务到线程池并返回Future
Future submit(Runnable, T) 提交任务到线程池并返回Future, 第二个参数会作为计算结果封装到Future
Future submit(Callable)提交任务到线程池并返回Future,call方法的计算结果会封装到Future
List invokeAll(Collection extends Callable>) 批量提交任务并返回计算结果
T invokeAny(Collectionextends Callable> tasks) 只执行其中一个任务并返回结果
void shutdown() 线程池不再接受新任务,继续运行正在执行中的任务及等待中的任务
List shutdownNow() 线程池不再接受新任务,继续运行正在执行中的任务,返回待执行的任务列表
shutdownNow:对正在执行的任务全部发出interrupt(),停止执行,对还未开始执行的任务全部取消,并且返回还没开始的任务列表
shutdown:当调用shutdown后,线程池将不再接受新的任务,但也不会去强制终止已经提交或者正在执行中的任务
任务性质可分为:CPU密集型任务,IO密集型任务,混合型任务。
可以先将线程池大小设置为参考值,再观察任务运行情况和系统负载、资源利用率来进行适当调整。
Runtime.getRuntime().availableProcessors():int获取CPU的核数