• java并发编程--Runnable Callable及Future


    1.Runnable

    Runnable是个接口,使用很简单:
    1. 实现该接口并重写run方法
    2. 利用该类的对象创建线程
    3. 线程启动时就会自动调用该对象的run方法
    通常在开发中结合ExecutorService使用,将任务的提交与任务的执行解耦开,同时也能更好地利用Executor提供的各种特性
    ExecutorService executor = Executors.newCachedThreadPool();
                       executor.submit(new Runnable() { 
                            public void run() {
                                   //TODO
                            }
                        });
    executor.shutdown();

    相对于继承Thread来创建线程方式,使用Runnable可以让你的实现类同时实现多个接口,而相对于Callable及Future,Runnable方法并不返回任务执行结果且不能抛出异常

    2.Callable

    与Runnable不同的是,Callable是个泛型参数化接口,并能返回线程的执行结果,且能在无法正常计算时抛出异常
    public interface Callable<V> {
        V call() throws Exception;
    }

    1. Callable并不像Runnable那样通过Thread的start方法就能启动实现类的run方法,所以它通常利用ExecutorService的submit方法去启动call方法自执行任务,而ExecutorService的submit又返回一个Future类型的结果,因此Callable通常也与Future一起使用

     ExecutorService pool = Executors.newCachedThreadPool();
         Future<String> future = pool.submit(new Callable{
               public void call(){
                       //TODO
               }
        });

    或者利用FutureTask封装Callable再由Thread去启动(少用)

    FutureTask<String> task = new FutureTask(new Callable{
            public void call(){
                  //TODO
            }
      });
     Thead thread = new Thread(task);
     thread.start();

    2. 通过Executors.callbale(Runnable task,T result)可以执行Runnable并返回"结果",但是这个结果并不是Runnable的执行结果(Runnable的run方法是void类型),而是执行者预定义的结果,这点可以从其实现原理RunnableAdpter源码看出

    public static <T> Callable<T> callable(Runnable task, T result) {
         if (task == null)
              throw new NullPointerException();
           return new RunnableAdapter<T>(task, result);//通过RunnableAdapter实现
    }
        
    static final class RunnableAdapter<T> implements Callable<T> {
         final Runnable task;
         final T result;
         RunnableAdapter(Runnable task, T result) {
                this.task = task;
                this.result = result;
         }
         public T call() {
            task.run();
            return result; //将传入的结果的直接返回
         }
       }

    Runnable与Callable不同点:

    1. Runnable不返回任务执行结果,Callable可返回任务执行结果
    2. Callable在任务无法计算结果时抛出异常,而Runnable不能
    3. Runnable任务可直接由Thread的start方法或ExecutorService的submit方法去执行
     
    3.Future

    Future保存异步计算的结果,可以在我们执行任务时去做其他工作,并提供了以下几个方法

    * cancel(boolean mayInterruptIfRunning):试图取消执行的任务,参数为true时直接中断正在执行的任务,否则直到当前任务执行完成,成功取消后返回true,否则返回false

    * isCancel():判断任务是否在正常执行完前被取消的,如果是则返回true

    * isDone():判断任务是否已完成

    * get():等待计算结果的返回,如果计算被取消了则抛出

    * get(long timeout,TimeUtil unit):设定计算结果的返回时间,如果在规定时间内没有返回计算结果则抛出TimeOutException

    使用Future的好处:

    1. 获取任务的结果,判断任务是否完成,中断任务

    1. Future的get方法很好的替代的了Thread.join或Thread,join(long millis)

    2. Future的get方法可以判断程序代码(任务)的执行是否超时,如:

    try{
          future.get(60,TimeUtil.SECOND);
     }catch(TimeoutException timeout){
          log4j.log("任务越野,将被取消!!");
          future.cancel();
     }

    4.FutureTask

    FutureTask实现了RunnableFuture接口,提供了即可以使用Runnable来执行任务,又可以使用Future执行任务并取得结果的构造器,所以可以利用FutureTask去封装Runnable或Callable对象,之后再submit任务
    FutureTask(Callable<V> callable)  
     FutureTask(Runnable runnable, V result)

    5.应用

    查找包含某关键字的文件个数:每个文件启动一个线程去查找关键字
    public class FileSearchTask {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            String path = args[0];
            String keyword = args[1];
            int c = 0;
            File[] files = new File(path).listFiles();
            ArrayList<Future<Integer>> rs = new ArrayList<>();
            for(File file: files){  //每个文件启动一个task去查找
                MatchCount count = new MatchCount();
                count.file = file;
                count.keyword = keyword;
                FutureTask<Integer> task = new FutureTask(count);
                rs.add(task); //将任务返回的结果添加到集合中
                Thread thread = new Thread(task);
                thread.start();
            }
    
            for(Future<Integer> f: rs){
                c += f.get(); //迭代返回结果并累加
            }
            System.out.println("包含关键字的总文件数为:" + c);
        }
    }
    
    class  MatchCount implements Callable<Integer>{
        public File file;
        public String keyword;
        private  Integer count = 0;
    
        public Integer call() throws Exception {   //call封装线程所需做的任务
            if(search(file))
                  count ++;
            return count;
        }
    
        public boolean search(File file){
            boolean founded = false;
            try(Scanner scanner = new Scanner(new FileInputStream(file))){
                while(!founded && scanner.hasNextLine()){
                    if (scanner.nextLine().contains(keyword))
                        founded = true;
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            return  founded;
        }
    }

    Java并发编程相关的例子:https://github.com/MOBIN-F/Thread

  • 相关阅读:
    关于浏览器的内核 版本 发展 详解
    js判断浏览器内核 及ie的版本问题
    插入flash代码
    大前端学习笔记【七】关于CSS再次整理
    其实,程序员没有我想象得那么简单(算是2016的简单总结吧...)
    大前端学习笔记整理【七】HTTP协议以及http与https的区别
    大前端学习笔记整理【六】this关键字详解
    大前端学习笔记整理【五】关于JavaScript中的关键字——this
    大前端学习笔记整理【五】rem与px换算的计算方式
    大前端学习笔记整理【四】LESS基础
  • 原文地址:https://www.cnblogs.com/longqingyang/p/6951105.html
Copyright © 2020-2023  润新知