线程的开销:
a、线程的创建与启动开销,与普通对象相比,Java线程还占用了额外的存储空间:栈空间;而且线程的启动会产生相应的线程调度开销
b、线程的销毁
c、线程的调度开销:线程的调度导致上下文切换,从而增加处理器资源的消耗,使得程序本身可以使用的处理器资源减少
d、 一个系统能够创建的线程总是受限于该系统所拥有的处理器数目,临界值总是处理器数目
public ThreadPoolExecutor(
intcorePoolSize, //指定线程池核心大小
intmaximumPoolSize, //指定最大线程池大小
longkeepAlive,
TimeUnitunit, //与keepAlive表示最大存活时间
BolckingQueue<Runnable>workQueue, //工作队列的阻塞队列,相当于生产消费者中的传输通道
ThreadFactorythreadFactory, //指定创建工作者线程的线程工厂
RejectedExecutionHandlerhandler) //见下表详细分析
|
实现类 |
所实现的处理策略 |
|
ThreadPoolExecutor.AbortPolicy |
直接抛出异常 |
|
ThreadPoolExecutor.DiscardPolicy |
丢弃当前被拒绝的任务 |
|
ThreadPoolExecutor.DiscardOldestPolicy |
将工作队列中最老的任务丢弃,重新尝试接受被拒绝的任务 |
|
ThreadPoolExecutor.CallerRunsPolicy |
在客户端线程中执行被拒绝的任务 |
初始状态下,客户端每提交一个任务,线程池就创建一个工作者线程来处理该任务,随着任务的增加、线程池大小也增加,达到核心线程池大小时,新来的任务就会存入工作队列中,这些缓存的任务由工作者线程负责取出进行执行;线程池将任务存入工作队列的时候调用的是BlockingQueue的非阻塞方法offer(E e),线程池是通过threadFactory.newThread方法来创建工作者线程的,在创建线程池时如果没有指定线程工厂、那么会使用Executors.defaultThreadFactory()所返回的默认线程工厂。
当线程池饱和状态时,即工作者队列满并且当前线程池大小达到最大线程池大小的情况下,客户端试图提交的任务会被拒绝,可以使用处理策略:
RejectedExecutionHandler接口的方法:
void rejectedExecution(Runnabler,ThreadPoolExecutor executor)
ThreadPoolExecutor.shutdown()/shutdownNow()可以用来关闭线程池
Shutdown()关闭线程池的时候,已提交的任务会被继续执行,而新提交的任务会像线程池饱和一样被拒绝
ShutdownNow()关闭线程池时,正在执行的任务会被关闭,已提交而在等待的任务也不会被执行
任务的处理结果、异常处理与取消
如果客户端关心任务的处理结果,可以使用ThreadPoolExecutor的submit方法来提交任务
public <T> Future<T> submit(Callable<T>task)
Callable接口相当于一个增强型的Runnable接口,定义的唯一方法:
V call() throws Exeception 表示任务在执行过程中可以抛出异常
将Runnable接口转Callable方法:Executors.callable(Runnable task, T result)
Future.get()方法可以用来获取task参数所指定的任务处理结果,被调用时、如果任务没有执行完毕会使当前线程暂停,直到任务执行结束或抛出异常,所以Future.get()是阻塞方法;所以一般在相应任务处理结果数据的那一刻才调用Future.get()方法
Boolean cancel(Boolean mayInterruptIfRunning)//任务取消是否成功
Boolean isDone()检测相应任务是否执行完毕
V get(long timeout, Timeout unit) throwsInterruptedExeception, ExecutionExeception,TimeoutExeception//作用与Future.get()相同
线程池监控
ThreadPoolExecutor提供的线程池监控相关方法
|
方法 |
用途 |
|
getPoolSize() |
获取当前线程池大小 |
|
getQueue() |
获取工作队列实例 |
|
getLargestPoolSize() |
获取工作者线程数曾达到的最大数 |
|
getAliveCount() |
获取线程池中正在执行任务的工作者线程数 |
|
getTaskCount() |
获取线程池接收到的任务数 |
|
getCompletedTaskCount() |
获取已处理完毕的任务数 |
线程池死锁
同一个线程池只能用于执行相互独立的任务,彼此有依赖关系的任务需要提交给不同的线程池执行以避免死锁
工作者线程的异常终止
如果任务是通过ThreadPoolExecutor.submit调用提交给线程池的,任务在执行过程中抛出了未捕获的异常也不会导致对其执行的工作者线程异常终止
如果任务是通过ThreadPoolExecutor.execute调用提交给线程池的,任务一旦抛出未捕获的异常,对其执行的工作者线程就会异常终止