Executor、Executors、Callable、FutureTask、ExecutorService

26 篇文章 0 订阅
25 篇文章 0 订阅

Executor

执行已提交的 Runnable 任务的对象。此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。通常使用 Executor 而不是显式地创建线程。

 public static void main(String[] args) {
   new Thread(new Test117(). new MyTask()).start();//之前总是需要创建一个线程,传入runable对象然后开启线程。
   }
  class MyTask implements Runnable{

    @Override
    public void run() {
        // TODO Auto-generated method stub
        System.out.println("我是任务");
    }

  }
execute

在该接口中只定义了一个方法。

void execute(Runnable command)
//在未来某个时间执行给定的命令。该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由 Executor 实现决定。 

所以上面这片代码执行的另外一种:

  Executor e = Executors.newSingleThreadExecutor();
      //Executors在后面会讲,在这里你只要知道这个方法可以返回一个可以操作的Executor对象
      e.execute(new Test117().new MyTask());
Executor的实现的子类
  1. AbstractExecutorService 该类还是一个抽象类
  2. ScheduledThreadPoolExecutor
  3. ThreadPoolExecutor
Executor 异步同步情况

Executor 接口并没有严格地要求执行是异步的,下面是在Java的开发文档上的一段内容。

其实看懂,还是比较容易,主要理解start和run两个方法就可以了,直接run方法是将该任务负载到调用的线程中执行(也就是主线程),这是同步,start是将该任务直接,开启一个新的线程,让后执行,这是异步。所以大家,就可以根据自己的需求,实现Executor 接口,自己根据需要来啦!

在最简单的情况下,执行程序可以在调用者的线程中立即运行已提交的任务:

 class DirectExecutor implements Executor {
     public void execute(Runnable r) {
         r.run();
     }
 }

更常见的是,任务是在某个不是调用者线程的线程中执行的。以下执行程序将为每个任务生成一个新线程。

 class ThreadPerTaskExecutor implements Executor {
     public void execute(Runnable r) {
         new Thread(r).start();
     }
 }
Executors

此类中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。可以用于上面对象的创建。相当于一个工厂类。

这里写图片描述

这个我就不说了,就是构造一个Executor对象,执行线程,用来处理runable和callable任务的。

这里写图片描述

defaultThreadFactory() 这个会默认自动为你创建线程。

newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

    ExecutorService es = Executors.newCachedThreadPool();
        for(int i = 0;i<10;i++) {
            try {
                Thread.sleep(i*1000);//我们在通过线程睡眠时间长,来拖下一个线程执行的顺序。
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        es.submit(new Callable<String>() {

            @Override
            public String call() throws Exception {
                // TODO Auto-generated method stub
                System.out.println("我是当前执行的线程"+Thread.currentThread().getName());
                return "我是执行返回";
            }
        });

    }
        es.shutdown();
     }

参考学习:https://www.cnblogs.com/Steven0805/p/6393443.html

这里写图片描述

由结果可以看出,这段代码由于每一个线程执行的间隔大,后者执行开始,前者已经完成,故重用之前的线程。

这里写图片描述

这里写图片描述

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

    ExecutorService es = Executors.newFixedThreadPool(3);
            for(int i = 0;i<10;i++) {
                es.submit(new Runnable() {

                    @Override
                    public void run() {
                        // TODO Auto-generated method stub
                    System.out.println("线程"+Thread.currentThread().getName()+"正在工作");
                    try {
                        Thread.sleep(8000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    }
                });
            }
            es.shutdown();
        }

这里写图片描述

由结果可以看出,执行中,线程操作数始终保持在你设置的最大线程数,如果超出,就在队列中等待。

这里写图片描述

创建一个定长线程池,支持定时及周期性任务执行。

ScheduledExecutorService es = Executors.newScheduledThreadPool(3);
        for(int i = 0;i<5;i++) {
            es.schedule(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub
                System.out.println("我是延迟执行的任务");
                }
            }, 3,TimeUnit.SECONDS);//前面是延迟的时间数,后面是时间数是按照哪种形式
        }
        es.shutdown();

这个有点像定时设定的任务

ScheduledExecutorService es = Executors.newScheduledThreadPool(3);
        for(int i = 0;i<5;i++) {
            es.scheduleAtFixedRate(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub
                System.out.println("我是延迟定时执行的任务");
                }
            }, 1,3,TimeUnit.SECONDS);//表示延迟1秒后,每3秒执行一次
        }
        es.shutdown();
Callable

这是一个接口,这个接口和runable接口,很类似,都是用于被线程执行而设计的,Callable和Runable 最大的区别就是:它能返回结果,并且能抛出异常。接口中只有一个不带参数的方法。

Callable 这个V就是Callable返回结果的类型。

   V call() throws Exception;
Future

这是一个接口,主要用于获取异步线程的结果,常常与Callable一起使用,因为Callable只是实现了可以返回一个值,必须还要通过Future接口的get方法才能取出来。

Future 这个泛型是返回的结果类型。

这是api上的一段说明:

它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。还提供了其他方法,以确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future

FutureTask

这个是Future的实现的子类,这个类实现了runable接口,所以他可以作为runable对象传到线程中执行。

可使用 FutureTask 包装 Callable 或 Runnable 对象。因为 FutureTask 实现了 Runnable,所以可将 FutureTask 提交给 Executor 执行。

构造方法

这里写图片描述

这里如果是处理的是runable对象,则我们需要将最后返回的结果用参数传给FutureTask ,然后它会在执行之后返回出来。

基于runable

    class MyTask implements Runnable{

        @Override
        public void run() {
            // TODO Auto-generated method stub
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }

    }

FutureTask<String> ft = new FutureTask<>(new Test118().new MyTask(),"ok");
        new Thread(ft).start();
        //在这里你可以处理其他的业务逻辑,然后在获取线程的值。
        try {
            System.out.println("我是取出来的结果"+ft.get());
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

基于callable的处理

    //启用一个新的线程随机产生一个随机数,然后返回。
        FutureTask<Integer> ft = new FutureTask<>(new Test118().new MtTask());
        new Thread(ft).start();
        //在这里你可以处理其他的业务逻辑,然后在获取线程的值。
        try {
            System.out.println("我是取出来的结果"+ft.get());
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    class MtTask implements Callable<Integer>{

        @Override
        public Integer call() throws Exception {
            // TODO Auto-generated method stub
            Thread.sleep(3000);
            return new Random().nextInt();
        }



    }
ExecutorService

提供了管理终止的方法,以及可为跟踪一个或多个异步任务执行状况而生成 Future 的方法。

可以关闭 ExecutorService,这将导致其拒绝新任务。提供两个方法来关闭 ExecutorService。shutdown() 方法在终止前允许执行以前提交的任务,而 shutdownNow() 方法阻止等待任务启动并试图停止当前正在执行的任务。终止时,执行程序没有任务在执行,也没有任务在等待执行,并且无法提交新任务。应该关闭未使用的 ExecutorService 以允许回收其资源。

常用方法

这里写图片描述

执行线程池中所有等待的callable任务,并返回Future结果的集合

这里写图片描述

这里的submit提交执行的runable或者callable的任务,并且返回一个Future结果。

这里写图片描述

方法 invokeAny 和 invokeAll 是批量执行的最常用形式,它们执行任务 collection

基于ExecutorService例子
      ExecutorService es = Executors.newCachedThreadPool();
          //创建了一个线程池
          List<Future<Integer>> list = new ArrayList();
          for(int i=0;i<10;i++) {
             Future<Integer> ft =  es.submit(new Test119().new  MyTask(i));
             //构建了十个任务
             list.add(ft);
             //将每个人物返回的Future结果,保存到集合
          }
es.shutdown();//执行完了之后,不接受任务,这就意味着,它就等待着回收资源。

          //遍历List中的Future结果集
          for(int i = 0;i<list.size();i++) {
              try {
                System.out.println(list.get(i).get());
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ExecutionException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
          }
    }

    class MyTask implements Callable<Integer>{
        int number;


        public MyTask(int number) {
            super();
            this.number = number;
        }


        @Override
        public Integer call() throws Exception {
            // TODO Auto-generated method stub
            System.out.println("我是线程"+Thread.currentThread().getName());
            Thread.sleep(2000);
            return (int) (Math.random()*10);
        }

    }

代码简单,只要是感受一下,在这十个人物执行过程中,我们不需要手动创建线程去操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值