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

时间: 2024-10-14 09:56:02

java并发编程--Runnable Callable及Future的相关文章

Java并发编程:Callable、Future和FutureTask(转)

Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦. 而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果. 今天我们就来讨论一下Callabl

15、Java并发编程:Callable、Future和FutureTask

Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦. 而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果. 今天我们就来讨论一下Callabl

[转载] Java并发编程:Callable、Future和FutureTask

转载自http://www.cnblogs.com/dolphin0520/p/3949310.html 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦. 而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果.

Java并发编程:Callable、Future和FutureTask的实现

启动线程执行任务,如果需要在任务执行完毕之后得到任务执行结果,可以使用从Java 1.5开始提供的Callable和Future下面就分析一下Callable.Future以及FutureTask的具体实现及使用方法源码分析基于JDK 1.7 一.Callable 与 Runnable java.lang.Runnable是一个接口,只有一个run()方法 public interface Runnable {    public abstract void run(); } run()方法的返

Java并发编程:Callable、Future和FutureTask

原文链接:http://www.cnblogs.com/dolphin0520/  作者:海子 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦. 而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果. 今天我们就

Java并发编程:Callable、Future和FutureTask及在项目中的引用

在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦. 而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果. 今天我们就来讨论一下Callable.Future和FutureTask三个类的使用方法.以下是本文的目录

Java并发编程:Callable、Future和FutureTask 实现龟兔赛跑

1.不清楚的看博客http://www.cnblogs.com/dolphin0520/p/3949310.html 我们使用上面的代码来实现一个龟兔赛跑 package com.weiyuan.test; import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurre

Java多线程编程:Callable、Future和FutureTask浅析

通过前面几篇的学习,我们知道创建线程的方式有两种,一种是实现Runnable接口,另一种是继承Thread,但是这两种方式都有个缺点,那就是在任务执行完成之后无法获取返回结果,那如果我们想要获取返回结果该如何实现呢?还记上一篇Executor框架结构中提到的Callable接口和Future接口吗?,是的,从Java SE 5.0开始引入了Callable和Future,通过它们构建的线程,在任务执行完成后就可以获取执行结果,今天我们就来聊聊线程创建的第三种方式,那就是实现Callable接口.

Java并发编程之Callable,Future,FutureTask

创建线程: 1. 继承Thread 2. 实现Runnable 仔细观察会发现void run() ,这个方法没有返回值,也就无法获得线程执行结果. Callable 返回结果并且可能抛出异常的任务.实现者定义了一个不带任何参数的叫做 call 的方法. Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的.但是 Runnable 不会返回结果,并且无法抛出经过检查的异常. V call() 计算结果,如果无法计算结果,则抛出一个异常. 跟Runna