java多线程——初识Executor框架

2019-01-22  本文已影响0人  Super昕

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
    在线程池中使用阻塞队列,主要是利用其阻塞操作,任务提交到线程池后在核心线程数不够用于执行所有任务时,会首先尝试再创建新的线程来执行任务,直到总的线程数超过线程池最大线程数,任务被缓存在阻塞队列中,如果有线程空闲了,就会从该队列中提取任务来执行;同时阻塞队列也是线程安全的,不会存在两个线程同时从阻塞队列中争抢同一个任务。
上一篇下一篇

猜你喜欢

热点阅读