Java 线程创建的方法之Callable接口

Java 创建多线程的四种方式

  1. 继承 Thread 类
  2. 实现 Runnable 接口
  3. 实现 Callable 接口
  4. 利用线程池

今天咱们来重点聊聊第三种实现方式,Callable 接口。

Callable接口

Callable 接口是从 JDK1.5 以后提供的一种线程创造方法,是多线程包 java.util.concurrent 包中的,用于多线程环境。

创建线程

Callable 接口包只有一个抽象方法 call()

但是发现 Thread 类的构造方法没有可以传 Callable 接口的构造器

这就体现 Java 面向接口编程的魅力所在,在 java.util.concurrent 包下有一个 FutureTask 类,它实现了 Runnable接口,同时构造方法可以传入一个 Callable 接口。所以创建一个 Callable 线程应该这样写:

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

public class CallableDemo {
    public static void main(String[] args) {
        FutureTask futureTask = new FutureTask<Integer>(new MyThread());
        Thread thread = new Thread(futureTask,"Callable");
        thread.start();
    }


}

class MyThread implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+" invoked");
        return 1024;
    }
}

结果

取返回值

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

public class CallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask = new FutureTask<Integer>(new MyThread());
        Thread thread = new Thread(futureTask,"Callable");
        thread.start();
        System.out.println("Callable return result: " + futureTask.get());
    }


}

class MyThread implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+" invoked");
        return 1024;
    }
}

结果

为什么会出现Callable接口

并发、异步导致出现了 Callable 接口

Callable接口和Runnable接口的区别

  1. Callable 规定的方法是call(),而 Runnable 规定的方法是run()
  2. Callable的任务执行后可返回值,而Runnable的任务是不能返回值的
  3. call()方法可抛出异常,而run()方法是不能抛出异常的
  4. 运行Callable任务可拿到一个Future对象,获取线程的执行结果

get()方法的注意事项

Callable 实现的线程可以通过 FutureTask 的 get() 方法获取线程返回值,如果没有计算完成就要结果,会导致堵塞,计算完成会才返回值。

示例

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

public class CallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask = new FutureTask<Integer>(new MyThread());
        Thread thread = new Thread(futureTask,"Callable");
        thread.start();
        System.out.println("Callable return result: " + futureTask.get());
        System.out.println(Thread.currentThread().getName()+" Done");
    }
}

class MyThread implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+" invoked");
        TimeUnit.SECONDS.sleep(2); //休眠2s
        return 1024;
    }
}

结果

正确的写法

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

public class CallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask = new FutureTask<Integer>(new MyThread());
        Thread thread = new Thread(futureTask,"Callable");
        thread.start();
        // 类似自旋锁
        while(!futureTask.isDone()){

        }
        System.out.println("Callable return result: " + futureTask.get());
        System.out.println(Thread.currentThread().getName()+" Done");
    }
}

class MyThread implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+" invoked");
        TimeUnit.SECONDS.sleep(2); //休眠2s
        return 1024;
    }
}

重复任务不重复计算

示例

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

public class CallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask = new FutureTask<Integer>(new MyThread());
        //线程1
        Thread thread1 = new Thread(futureTask,"Callable 1");
        thread1.start();
        //线程2
        Thread thread2 = new Thread(futureTask,"Callable 2");
        thread2.start();
        // 类似自旋锁
        while(!futureTask.isDone()){

        }
        System.out.println("Callable return result: " + futureTask.get());
        System.out.println(Thread.currentThread().getName()+" Done");
    }
}

class MyThread implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+" invoked");
        TimeUnit.SECONDS.sleep(2); //休眠2s
        return 1024;
    }
}

结果

观察结果可以看到只有一个线程进入了 call() 方法并返回了结果,所以对于重复的 FutureTask 任务,是不会进行重复计算的。

发布了80 篇原创文章 · 获赞 55 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/cong____cong/article/details/104752954