Executor框架概览
如图所示,Executor接口作为框架的根定义了execut()方法,该方法负责执行提交的任务,ExecutorService接口在该接口的基础上增加了一些服务方法,AbstractExecutorService抽象类实现了一些通用方法,ScheduledExecutorService接口继承了ExecutorService接口并增加了一些定时执行任务的方法,ThreadPoolExecutor类扩展AbstractExecutorService类提供了丰富的使用方法,ScheduledThreadPoolExecutor类在实现ScheduledExecutorService接口扩展ThreadPoolExecutor类,提供了丰富的定时任务方法。
Executor1.png
ThreadPoolExecutor类定义的方法视图:
方法.png
Executor框架解决了任务提交与任务执行的耦合问题,我们从一个简单的计算二维数组各行和,最终计算整个数组元素和的例子开始:
public class DemoMain {
//定义需要计算的二维数组
private static int [][] array= {{1,2,3},{4,5,6},{7,8,9}};
/**
* @param i
* @return 获取各行和
*/
private static int getRowSum(int i) {
int result=0;
for(int j=0;j<array.length;j++) {
result+=array[i][j];
}
return result;
}
/**
* @author 54353
* 线程任务类
*/
public static class Task implements Runnable{
//定义计算行
private int row;
public Task(int row) {
this.row=row;
}
@Override
public void run() {
int sum=getRowSum(row);
System.out.println("线程"+Thread.currentThread().getName()+"计算数组第"+row+"行的和为"+sum);
}
}
public static void main(String[] args) {
Task task0=new Task(0);
Task task1=new Task(1);
Task task2=new Task(2);
new Thread(task0).start();
new Thread(task1).start();
new Thread(task2).start();
}
}
输出结果为:
线程Thread-0计算数组第0行的和为6
线程Thread-2计算数组第2行的和为24
线程Thread-1计算数组第1行的和为15
在以上三行三列的数组计算中,我们计算每行数组和时都启用一个线程独立的运算,需要手动创建三个线程与计算任务Task绑定到一起,再调用star()方法执行任务,在这个过程中线程类与任务类的绑定,任务的执行都与Thread对象相耦合。下图是Thread类给我们提供的可以调用的方法,我将常用的标注了出来:
Thread类方法.png使用Executor框架,修改main方法如下:
public static void main(String[] args) {
//定义一个线程池对象,使用Executors工具类提供的无界队列构造线程池;
ExecutorService executor=Executors.newCachedThreadPool();
for(int i=0;i<array.length;i++) {
executor.execute(new Task(i));
}
}
利用执行器执行任务,已经为我们屏蔽了具体线程的创建,使得任务的提交与任务的执行解耦,执行器提供了丰富的任务执行控制方法:
ExecutorService.png
在改进中使用了Executors工具类的线程池 newCachedThreadPool(),该工具类中定义了以下几种线程池以供直接使用:
Executors框架定义的返回执行器的方法1.png Executors框架定义的返回线程池的方法2.png
在java.util.concurrent包中定义了ThreadPoolExecutor类,可以根据该类的构造函数,设置不同参数,得到自定义的线程池类型:
ThreadPoolExecutor构造函数.png
线程池组合使用了阻塞队列,下图给出了阻塞队列的UML图:
阻塞队列.png
阻塞队列与一般队列相比,增加了两种操作:获取元素时等待队列变为非空,以及存储元素时等待空间变得可用。所以阻塞队列拥有两套插入、移除的操作支持阻塞与非阻塞运用:
阻塞队列的操作.png
在线程池中使用阻塞队列,主要是利用其阻塞操作,任务提交到线程池后在核心线程数不够用于执行所有任务时,会首先尝试再创建新的线程来执行任务,直到总的线程数超过线程池最大线程数,任务被缓存在阻塞队列中,如果有线程空闲了,就会从该队列中提取任务来执行;同时阻塞队列也是线程安全的,不会存在两个线程同时从阻塞队列中争抢同一个任务。
网友评论