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

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

🍊为何要使用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);
    }
}

👁‍🗨️结果:

image.png

😮通过结果发现,没有输出我们预期的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);
        }
    }
}

👁️结果:可以看到,结果符合我们预期的结果

image.png

❗❗❗但是使用这种方式来达到我们预期结果,使用到了加锁释放锁,线程通信一系列操作,比较繁琐,所以我们需要使用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 callable = new Callable() {
            @Override
            public Integer call() throws Exception {
                n++;
                return n;
            }
        };
        FutureTask task = new FutureTask<>(callable);
        Thread t = new Thread(task);
        t.start();
        Integer ret = task.get(); //task.get()会让main线程等待,等待t线程执行完并获取返回结果后再继续执行main线程后续代码
        System.out.println(ret);
    }
}

👁️执行结果:符合我们预期的结果

image.png

示例二:我们创建线程执行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 callable = new Callable() {
            @Override
            public Integer call() throws Exception { //重写call方法
                int sum = 0;
                for(int i = 1;i <= 50;i++){
                    sum += i;
                }
                return sum;  //返回值
            }
        };
        //参数传入Callable对象callable
        FutureTask task = new FutureTask<>(callable); //创建FutureTask对象来保存返回结果
        Thread t = new Thread(task); //创建线程,参数传入FutureTask对象task
        t.start();
        System.out.println(task.get()); //task.get()获取到结果,并打印输出
    }
}


相关文章
|
26天前
|
Java 程序员
Java社招面试中的高频考点:Callable、Future与FutureTask详解
大家好,我是小米。本文主要讲解Java多线程编程中的三个重要概念:Callable、Future和FutureTask。它们在实际开发中帮助我们更灵活、高效地处理多线程任务,尤其适合社招面试场景。通过 Callable 可以定义有返回值且可能抛出异常的任务;Future 用于获取任务结果并提供取消和检查状态的功能;FutureTask 则结合了两者的优势,既可执行任务又可获取结果。掌握这些知识不仅能提升你的编程能力,还能让你在面试中脱颖而出。文中结合实例详细介绍了这三个概念的使用方法及其区别与联系。希望对大家有所帮助!
163 60
|
4月前
|
Java C++
【多线程】JUC的常见类,Callable接口,ReentranLock,Semaphore,CountDownLatch
【多线程】JUC的常见类,Callable接口,ReentranLock,Semaphore,CountDownLatch
51 0
|
6月前
|
Java
Java中Runnable和Callable有什么不同
【8月更文挑战第9天】Java中Runnable和Callable有什么不同
32 1
|
7月前
|
存储 缓存 安全
(八)深入并发之Runnable、Callable、FutureTask及CompletableFuture原理分析
关于Runnable、Callable接口大家可能在最开始学习Java多线程编程时,都曾学习过一个概念:在Java中创建多线程的方式有三种:继承Thread类、实现Runnable接口以及实现Callable接口。但是实则不然,真正创建多线程的方式只有一种:继承Thread类,因为只有`new Thread().start()`这种方式才能真正的映射一条OS的内核线程执行,而关于实现Runnable接口以及实现Callable接口创建出的Runnable、Callable对象在我看来只能姑且被称为“多线程任务”,因为无论是Runnable对象还是Callable对象,最终执行都要交由Threa
158 1
|
9月前
|
安全 Java
多线程(CAS, ABA问题, Runnable & Callable & 僵尸线程 & 孤儿进程)
多线程(CAS, ABA问题, Runnable & Callable & 僵尸线程 & 孤儿进程)
82 1
|
8月前
|
存储 Java
详细解读Callable接口及Futrue接口详解
详细解读Callable接口及Futrue接口详解
29 0
|
6月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
3月前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
3月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
3月前
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
96 4