Java线程实现的第三种方式Callable方式与结合Future获取返回值

   多线程的实现方式有实现Runnable接口和继承Thread类(实际上Thread类也实现了Runnable接口),但是Runnable接口的方式有两个弊端,第一个是不能获取返回结果,第二个是不能抛出exception。但是Callable接口很好的解决了上面的问题。下面介绍Callable接口的使用方法。

0.我们先看JDKAPI对callable接口的解释:

public interface Callable<V>

  返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。

  Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。

  Executors 类包含一些从其他普通形式转换成 Callable 类的实用方法。

方法解释:

call

V call()
       throws Exception
计算结果,如果无法计算结果,则抛出一个异常。

 

返回:
计算的结果
抛出:
Exception - 如果无法计算结果

 

1.第一个实现Callable接口开启线程的用法:(不接受返回值,只是开启线程执行任务)

package threadTest;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * 实现callable接口,实现Callable接口
 * 
 *
 */
public class MyCallable implements Callable<String> {

    /**
     * 实现call方法,接口中抛出异常。因为子类不可以比父类干更多的坏事,所以子类可以不抛出异常
     */
    @Override
    public String call() {
        System.out.println(Thread.currentThread().getName() + "   执行callable的call方法");
        return "result";
    }

    public static void main(String[] args) {
        // 1.创建callable对象
        Callable<String> myCallable = new MyCallable();
        // 2.由上面的callable对象创建一个FutureTask对象
        FutureTask<String> oneTask = new FutureTask<String>(myCallable);
        // 3.由FutureTask创建一个Thread对象
        Thread t = new Thread(oneTask);
        // 4.开启线程
        t.start();
    }

}

 结果:

Thread-0   执行callable的call方法

解释:

  (1)Callable接口不用解释了,就是一个类似于Runnable接口的接口,只有一个call方法,此方法有返回值,可以抛出异常。

  (2)Futuretask类查看:实现了RunnableFuture接口,RunnableFuture接口继承于Runnable接口和Future接口:所以Futuretask是Runnable的对象(子类的对象也是父类的对象)

public class FutureTask<V> implements RunnableFuture<V> {

查看此类的构造方法:初始化成员变量callable

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

查看此类的run方法:(调用Callable的call()方法)

    public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

    JDKAPI对此类的解释:

       

  (3)RunnableFuture接口:

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

  (4)Runnable接口不用解释了,Future接口的解释如下:===也就是Future用于获取Callable执行的返回结果

package java.util.concurrent;
public interface Future<V> {

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

   JDKAPI的解释:

public interface Future<V>

  Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。还提供了其他方法,以确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。

用法示例(注意,下列各类都是构造好的。)

 

 interface ArchiveSearcher { String search(String target); }
 class App {
   ExecutorService executor = ...
   ArchiveSearcher searcher = ...
   void showSearch(final String target)
       throws InterruptedException {
     Future<String> future
       = executor.submit(new Callable<String>() {
         public String call() {
             return searcher.search(target);
         }});
     displayOtherThings(); // do other things while searching
     try {
       displayText(future.get()); // use future
     } catch (ExecutionException ex) { cleanup(); return; }
   }
 }
 

FutureTask 类是 Future 的一个实现,Future 可实现 Runnable,所以可通过 Executor 来执行。例如,可用下列内容替换上面带有 submit 的构造:

     FutureTask<String> future =
       new FutureTask<String>(new Callable<String>() {
         public String call() {
           return searcher.search(target);
       }});
     executor.execute(future);

   (5)Thread类可以接受一个Runnable接口,上面代码FutureTask实现了RunnableFuture接口,RunnableFuture接口继承于Runnable接口,所以可以接受FutureTask对象。

 

 2.使用ExecutorService、Callable、Future实现有返回结果的线程

 1.单线程的获取返回结果:

package threadTest;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 实现callable接口,实现Callable接口
 * 
 *
 */
public class MyCallable implements Callable<String> {

    /**
     * 实现call方法,接口中抛出异常。因为子类不可以比父类干更多的坏事,所以子类可以不抛出异常
     */
    @Override
    public String call() {
        System.out.println(Thread.currentThread().getName() + "   执行callable的call方法");
        return "result";
    }

    public static void main(String[] args) {
        test1();
    }

    /**
     * 单个线程
     */
    public static void test1() {
        // 1.创建固定大小的线程池
        ExecutorService es = Executors.newFixedThreadPool(1);
        // 2.提交线程任务,用Future接口接受返回的实现类
        Future<String> future = es.submit(new MyCallable());
        // 3.关闭线程池
        es.shutdown();
        // 4.调用future.get()获取callable执行完成的返回结果
        String result;
        try {
            result = future.get();
            System.out.println(Thread.currentThread().getName() + "\t" + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

 结果:

pool-1-thread-1 执行callable的call方法
main result

2.多个线程的执行返回结果:

package threadTest;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 实现callable接口,实现Callable接口
 * 
 *
 */
public class MyCallable implements Callable<String> {

    /**
     * 实现call方法,接口中抛出异常。因为子类不可以比父类干更多的坏事,所以子类可以不抛出异常
     */
    @Override
    public String call() {
        System.out.println(Thread.currentThread().getName() + "   执行callable的call方法");
        return "result";
    }

    public static void main(String[] args) {
        test2();
    }
/**
     * 多个线程
     */
    public static void test2() {
        // 1.创建固定大小的线程池(5个)
        int threadNum = 5;
        ExecutorService es = Executors.newFixedThreadPool(threadNum);
        // 2.提交线程任务,用Future接口接受返回的实现类
        List<Future<String>> futures = new ArrayList<Future<String>>(threadNum);
        for (int i = 0; i < threadNum; i++) {
            Future<String> future = es.submit(new MyCallable());
            futures.add(future);
        }
        // 3.关闭线程池
        es.shutdown();
        // 4.调用future.get()获取callable执行完成的返回结果
        for (Future<String> future : futures) {
            try {
                String result = future.get();
                System.out.println(Thread.currentThread().getName() + "\t" + result);
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

}

 结果:

pool-1-thread-1 执行callable的call方法
pool-1-thread-2 执行callable的call方法
pool-1-thread-4 执行callable的call方法
pool-1-thread-3 执行callable的call方法
main result
pool-1-thread-5 执行callable的call方法
main result
main result
main result
main result

总结:

   ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。

 

线程池的使用参考:https://www.cnblogs.com/qlqwjy/p/9470414.html

 

posted @ 2019-01-10 11:05  QiaoZhi  阅读(3166)  评论(0编辑  收藏  举报