-
线程池概念与作用
-
为什么需要线程池(池化资源技术产生原因)
解决线程生命周期开销问题和资源不足问题。消除线程创建带来的延迟,使应用程序响应更快。
-
一个比较简单的线程池构成
- 线程池管理器
-创建、销毁并管理线程池,将工作线程放入线程池 - 工作线程
-一个可以循环执行任务的线程,没有任务时等待 - 任务队列
-提供缓冲机制,将没有处理的任务放入任务队列中 - 任务接口
-每个任务必须实现的接口,主要规定任务入口、任务执行完后的收尾工作、任务的执行状态等,工作线程通过该接口调度任务的执行。
- 线程池管理器
-
-
实现基础的线程池 一个工作线程 一个调度器
- 一个简单的工作线程 和一个 简单的线程池调度器
package com.qinhan.threadingThreadPool;
//执行线程:这是我们的工作线程
public class ExecutorThread extends Thread {
//用于描述线程是否处于运行状态。
boolean runningFlag=false;
private Runnable callBack=null;
public void setCallBack(Runnable callBack) {
this.callBack=callBack;
}
public synchronized void setRunning(boolean flag) {
runningFlag=flag;
if(flag) {
//如果flag为true说明希望执行线程处理某一个具体的任务,则唤醒某个线程。
this.notify();
}
}
public synchronized void run() {
try {
while(true) {
//任务执行结束后阻塞本线程
if(!runningFlag) {
this.wait();
}else {
callBack.run();
setRunning(false);
}
}
}catch (Exception e) {
// TODO: handle exception
}
}
}
package com.qinhan.threadingThreadPool;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ThreadPoolExecutor {
//池容器
private List<ExecutorThread> threads;
//构造方法:创建指定数量的线程并启动并将其放入池容器。
private ThreadPoolExecutor(int threadCount) {
threads=Collections.synchronizedList(new ArrayList<ExecutorThread>());
for(int i=1;i<=threadCount;i++) {
ExecutorThread thread=new ExecutorThread();
threads.add(thread);
thread.start();
}
}
//从线程池中选择一个空闲的线程执行任务
public void execute(Runnable callBack) {
int i;
for(i=0;i<threads.size();i++) {
ExecutorThread currentThread=threads.get(i);
if(!currentThread.runningFlag) {
currentThread.setCallBack(callBack);
currentThread.setRunning(true);
return;
}
}
if(i==threads.size()) {
System.out.println("线程资源已经用完,请稍等片刻再申请资源");
}
}
}
-
线程池调度器
-
Executor :JDK5的异步框架
-
作用:将任务提交和任务执行进行解耦;使得开发人员不再关注各类任务线程的实现,而将后台异步执行的内容抽象为单个任务。执行任务的人只需把Task描述清除后提交即可。
流程:提交Callable对象给ExecutorService(如常用的线程池ThreadPoolExecutor),得到一个Future对象,调用Future对象的get()等待执行结果。
-
Executor框架的重要核心接口和类
- Executor接口 :
- 一个可提交可执行任务的工具
- 解耦任务提交和任务执行
- 主要替代显示的创建和运行线程
- ExecutorService接口
- 提供异步的管理一个或多个线程终止、执行过程(Future)的方法
- 和其实现类提供简便的方式提交任务并获取任务执行结果,封装任务执行的全部过程。
- ScheduledExecutorService接口
- 主要工具类 Executors用来获取实现ExecutorService接口的子类
- 提供一系列工厂方法用于创建任务执行器,返回的任务执行器都实现ExecutorService接口(且绝大部分执行器都完成池化操作。)
- Executor接口 :
-
-
生成ExecutorService实现类 内置的常见工厂方法及生成的任务调度器特征 步骤1
// Executors类的静态方法 获取 ExecutorService实现类 ExecutorService newFixedThreadPool(int nThreads) //创建固定数目线程的线程池 ExecutorService newCachedThreadPool() //创建一个可缓存的线程池 ExecutorService newSingleThreadExecutor() //创建一个单线程化的Exrcutor 保证线程顺序执行 ScheduledExecutorService newScheduledThreadPool(int corePoolSize)//创建一个支持定时及周期性的人物执行的线程池,多数情况可用来替代Timer类。
- ExecutorService的方法 步骤2
- execute(Runnable)
- 异步方式执行,无法获取返回结果
- submit(Runnable)
- 返回一个Future对象,该对象可以判断Runnable任务是否结束执行。
- submit(Callable)
- Callable可以返回一个结果
- 返回值同样从该方法返回的Future对象中获取。
- invokeAny(…)
- 参数:接收一个包含Callable对象的集合
- 返回值:返回集合中某个Callable的结果,而且无法保证调用之后返回的结果是集合中的哪个Callable结果。看自写的代码。
- 如果一个任务运行完毕或抛出异常,方法会取消其他Callable的执行。也就是找到一个东西,那么不需要再找了。
- invokeAll(…)
- 参数:Callable对象集合
- 返回值:返回一个包含Future对象的集合,可通过该集合管理每一个Callable的执行结果。找出所有东西
- 注意:任务可能因为异常而导致运行结束,所以它可能不是真的成功运行。而这个我们无法通过Future对象来了解这个差异。
-
关闭ExecutorService 步骤3
- 使用完毕,我们应该关闭它,保证其中的线程不会继续保持运行状态
- 场景一:如果程序通过main主线程启动,主线程退出,但还有一个活动的ExecuterService存在于程序中;思考:会如何? 解答:程序将继续保持运行状态。存在于ExecutorService中的活动线程会阻止JVM关闭。
- shutdown方法:关闭ExecutorService中的线程;注意:它并不会马上关闭,而是不再接收新的任务,一旦所有线程结束执行当前任务,它才会真的关闭。而所有在shutdown方法前提交到ExecutorService的任务都会执行。
- shutdownNow方法:希望立即关闭ExecutorService。注意:尝试马上关闭所有正在执行的任务,并且跳过所有已经提交但是还没有运行的任务。对于正在执行的任务,是否能够成功关闭是无法保证的。
-
-
Runnable与Callable
- 区别:前者方法为run,不返回任何任务。后者方法为call,返回执行后的结果。
- 都符合函数式接口的标准