面试官:除了继承Thread类和实现Runnable接口,你知道使用Callable接口的方式来创建线程吗?

🍊为何要使用Callable来创建线程?

对一个变量n,初始化为0,我们使用实现Runnable接口的方式创建一个线程来对其进行一次n++操作,看看能得到我们预期的结果吗?

java 复制代码
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在什么时候修改不清楚

我们使用线程通信的方式对上述代码进行改造来达到我们预期的结果

java 复制代码
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进行改造

java 复制代码
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的用法

❗❗❗结合注释理解

java 复制代码
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()获取到结果,并打印输出
    }
}
相关推荐
神仙别闹32 分钟前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭1 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫1 小时前
泛型(2)
java
超爱吃士力架1 小时前
邀请逻辑
java·linux·后端
南宫生1 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石1 小时前
12/21java基础
java
李小白662 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp2 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
装不满的克莱因瓶2 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
n北斗2 小时前
常用类晨考day15
java