🍊为何要使用Callable来创建线程?
对一个变量n,初始化为0,我们使用实现Runnable接口的方式创建一个线程来对其进行一次n++操作,看看能得到我们预期的结果吗?
public class MyCallable { private static int n; public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { n++; } }); t1.start(); System.out.println(n); } }
👁🗨️结果:
😮通过结果发现,没有输出我们预期的1,这是因为main线程和t1线程是并发执行的,n在什么时候修改不清楚
我们使用线程通信的方式对上述代码进行改造来达到我们预期的结果
public class MyCallable { private static int n; public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new Runnable() { @Override public void run() { synchronized (MyCallable.class){ n++; MyCallable.class.notify(); } } }); t1.start(); synchronized (MyCallable.class){ while(n == 0){ MyCallable.class.wait(); } System.out.println(n); } } }
👁️结果:可以看到,结果符合我们预期的结果
❗❗❗但是使用这种方式来达到我们预期结果,使用到了加锁释放锁,线程通信一系列操作,比较繁琐,所以我们需要使用Callable接口创建线程的方式来返回线程执行的结果
🍉Callable的使用方式
🍀创建一个Callable(泛型)对象 ,重写带返回值的call方法
🍀创建一个FutureTask任务对象task,参数传入创建的Callable对象
🍀使用Thread创建线程,参数传入task对象
🍀返回值为task.get(),当前线程阻塞等待task执行完毕并返回结果后,再执行当前线程后续任务
🍵关于Callable:
🔌Callable和Runnable都是描述一个任务,Callable描述的是带有返回值的任务,Runnable描述的是不带返回值的任务
🔌Callable重写call方法,Runnable重写run方法
🔌Callable搭配FutureTask来使用,FutuerTask用来保存Callable的返回结果,因为Callable往往是在另一个线程中执行的,啥时候执行完并不清楚,所以需要使用FutuerTask来保存执行返回结果
🍋Callable的使用实例
示例一:先对上述执行一次n++的操作代码使用Callable进行改造
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class MyCallable { private static int n; public static void main(String[] args) throws InterruptedException, ExecutionException { Callable<Integer> callable = new Callable<Integer>() { @Override public Integer call() throws Exception { n++; return n; } }; FutureTask<Integer> task = new FutureTask<>(callable); Thread t = new Thread(task); t.start(); Integer ret = task.get(); //task.get()会让main线程等待,等待t线程执行完并获取返回结果后再继续执行main线程后续代码 System.out.println(ret); } }
👁️执行结果:符合我们预期的结果
示例二:我们创建线程执行1+2+3+...+50的操作并获取到结果,来进一步理解Callable的用法
❗❗❗结合注释理解
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class MyCallable { public static void main(String[] args) throws ExecutionException, InterruptedException { Callable<Integer> callable = new Callable<Integer>() { @Override public Integer call() throws Exception { //重写call方法 int sum = 0; for(int i = 1;i <= 50;i++){ sum += i; } return sum; //返回值 } }; //参数传入Callable对象callable FutureTask<Integer> task = new FutureTask<>(callable); //创建FutureTask对象来保存返回结果 Thread t = new Thread(task); //创建线程,参数传入FutureTask对象task t.start(); System.out.println(task.get()); //task.get()获取到结果,并打印输出 } }