java 多线程:Callable接口;FutureTask类实现对象【Thread、Runnable、Callable三种方式实现多线程的区别】

接口介绍:

Java5开始,Java提供了Callable接口,像是Runnable接口的增强版,Callable接口提供了一个 call()方法可以作为线执行体.
call()方法比run()方法功更强大。call()方法可以有返回值,call()方法可以抛出异常

 实现方法:

  1. 创建Callable接口实现类对象
  2. 创建FutureTask类实现对象
  3. 创建Thread类实现对象
  4. 调用Thread类实现对象start()方法提交线程任务
 
示例:
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @ClassName CallableExample
 * @projectName: object1
 * @author: Zhangmingda
 * @description: XXX
 * date: 2021/4/19.
 */
public class CallableExample {
    public static void main(String[] args) {
        /**
         * 实现Callable接口必须重写call方法。
         */
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return null;
            }
        };
        /**
         * 该接口又是函数式接口只有一个call方法,可以用lambda表达式替代如下
         */
        Callable<Integer> callable1 = () -> null;

        /**
         * 测试多线程实际应用:计算1+2+3+...+100的和
         * 接口实现类对象callable2
         */
        Callable<Integer> callable2 = () -> {
            int num = 0;
            for(int i=1; i<=100; i++){
                num +=i;
            }
            System.out.println("I am "+ Thread.currentThread().getName() + "  My result:" + num);
            return num;
        };
        //使用Set集合存储计划任务FutureTask类对象,便于获取结果
        Set<FutureTask> futureTasks = new HashSet<>();
        //跑10个线程
        for(int i=0; i<10; i++){
            //通过callable接口实现类 创建计划任务FutureTask实现类对象
            FutureTask<Integer> futureTask = new FutureTask<>(callable2);
            //向集合中添加计划任务类对象
            futureTasks.add(futureTask);
            //通过FutureTask实现类创建线程实体类Thread类对象,并提交给jvm运行
            new Thread(futureTask).start();
        }

        //获取每个任务的结果计算总和
        int resultCount =0;
        for(FutureTask<Integer> futureTask :futureTasks){
            try {
                //计算所有任务结果总和
                resultCount += futureTask.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        System.out.println("10个任务计算总和:" + resultCount);
    }
}

 Thread、Runnable、Callable三种方式实现多线程的优势区别

  1. 继承:采用Thread方式实现的线程不能继承其他父类,采用Runnable和Callable接口的可以继承其他父类,但是编程上采用Thread的方式可以直接使用getName()方法,而采用Runnable和Callable接口的方式需要先获取Thread.currentThread();
  2. 共享:采用Runnable和Callable的方式,可以多个线程公用一个Target对象,而采用Thread的方式不能,所以非常适合多个相同线程来处理同一份资源的情况
  3. 返回值:如果需要线程有返回值的需要使用Callable的方式。