线程池ThreadPoolExecutor的使用方法
方法我们通过继承Thread类和实现runnable接口或者callable接口三种方式实现。
继承Thread类实际上也是实现了runnable接口,被继承的类主要是实现run()方法,通过start()方法调用。
而callable接口是属于Executor
对比与Runnable接口功能的区别是:
(1).Callable可以在任务结束后提供一个返回值,Runnable没有这个功能 (2).Callable中的call()方法可以抛出异常,而Runnable的run()方法不能抛出异常 (3).运行Callable可以拿到一个Future对象,Future独享表示异步计算的结果,它提供了检查计算是否完成的方法。由于线程属于异步计算模型,因此无法从别的线程中得到函数 的返回值,在这种情况下,就可以使用Future来监视目标线程调用call()方法的情况,放调用Future的get()方法以获取结果时,当前线程就会阻塞,知道call()方法结束返回结果。
我们可以使用多线程来达到最优效率,但是,线程不是越多就越好,线程过多,创建和销毁就会消耗系统的资源,也不方便管理。
除此之外,多线程还会造成并发问题,线程并发数量过多,抢占系统资源从而导致阻塞。
我们将线程放入线程池,由线程池对线程进行管理,可以对线程池中缓冲的线程进行复用,这样,就不会经常去创建和销毁线程了,从而省下了系统的资源。
线程池能够有效的控制线程并发的数量,能够解决多线程造成的并发问题。
ThreadPoolExecutor
Java提供了ThreadPoolExecutor线程池类,他的构造方法有四个,主要是参数不同
corePoolSize为的线程池最大核心线程数量
maximumPoolSize为最大线程池数量
keepAliveTime 当活跃线程数大于核心线程数时,空闲的多余线程最大存活时间
unit 存活时间的单位
workQueue 存放线程的工作队列
handler:超出线程范围和队列容量的任务的处理程序(拒绝策略)
当线程放入线程池中,当线程的数量已经等于最大的核心线程数量,此线程就会放入线程队列当中,等待线程的调用。
线程池的最大线程数(线程总数,maximumPoolSize)= 核心线程数(corePoolSize)+非核心线程数
核心线程是永远不会被线程池丢弃回收(即使核心线程没有工作),非核心线程则是超过一定时间(keepAliverTime)则就会被丢弃
Excutors工具类
java提供了Excutors工具类,适用于一下小项目,对于一些大型程序还是需要自己创建ThreadPoolExecutor类,因为Excutors可能会造成OOM(堆内存溢出)
FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致OOM。
CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。
方法 | 说明 |
newFixedThreadPool(int nThreads) | 创建固定大小的线程池 |
newSingleThreadExecutor() | 创建只有一个线程的线程池 |
newCachedThreadPool() | 创建一个不限线程数上限的线程池,任何提交的任务都将立即执行 |
newScheduledThreadPool(int nThreads) | 创建一个支持定时、周期性或延时任务的限定线程数目的线程池 |
newSingleThreadScheduledExecutor() | 创建一个支持定时、周期性或延时任务的单个线程的线程池 |
线程池获取执行结果
把线程提交给线程池中,有两种方法,一种是submit,另外一种则是execute
execute没有返回值,性能会好很多,submit返回一个Future对象,如果想知道线程结果就使用submit提交,而且它能在主线程中通过Future的get方法捕获线程中的异常。
这两种方法的参数必须要实现runnable接口或者是callable对象
线程池的处理结果、以及处理过程中的异常都被包装到Future中,并在调用Future.get()方法时获取,执行过程中的异常会被包装成ExecutionException。
关闭线程池
可以通过shutdown()或者shutdownNow()方法
shutdown() : 不再接受新的任务,之前提交的任务等执行结束再关闭线程池
shutdownNow() :不再接受新的任务,试图停止池中的任务再关闭线程池,返回所有未处理的线程list列表。
Semaphore 的使用方式
Semaphore 是 synchronized 的加强版,作用是控制线程的并发数量。(并发控制信号量)
我们可以在主要执行任务的call()方法或者run()方法中使用semaphore来控制并发。
semaphore的构造方法有两个
第一个构造方法的参数为同一时刻允许线程进入的个数,如果为1,就相当于单个线程。
第二个构造方法的参数为线程公平性isFair
isFair 的意思就是,是否公平,获得锁的顺序与线程启动顺序有关,就是公平,先启动的线程,先获得锁。isFair 不能100% 保证公平,只能是大概率公平。
isFair 为 true,则表示公平,先启动的线程先获得锁。
在 semaphore.acquire() 和 semaphore.release()之间的代码,同一时刻只允许制定个数的线程进入, 因为semaphore的构造方法是1,则同一时刻只允许一个线程进入,其他线程只能等待。
acquire: