Java中Runnable和Callable分别什么时候使用

 更新时间:2023年08月07日 15:03:26   作者:古时的风筝  
提到 Java 就不得不说多线程了,就算你不想说,面试官也得让你说呀,那说到线程,就不得不说Runnable和Callable这两个家伙了,二者在什么时候使用呢,下面就来和简单讲讲

提到 Java 就不得不说多线程了,就算你不想说,面试官也得让你说呀,对不对。那说到多线程,就不得提线程了(这不废话吗)。那说到线程,就不得不说RunnableCallable这两个家伙了。

说熟悉也是真熟悉,在刚学习多线程的时候,第一个例子大概就是下面这样子的。

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("执行线程" + Thread.currentThread().getName());
    }
}).start();

看到了 Runnable的身影,有时候还会看到Callable的。

但是说很熟悉吧,印象也不是很大,好像就用了一下这两位的名号,然后剩下的部分就跟他俩没啥关系了。

今天,我们就来看看这两位到底是什么,有什么区别,什时候应该用 Runnable,什么时候又应该用 Callable

Runnable

自从Java诞生,Runnable就存在了,元老中的元老了,在 1.5之前,如果你想使用线程,那必须要实现自 Runnable。因为到了 JDK1.5,JDK 才加入了Callable

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

是不是接口非常简单,就一个抽象方法。

其实还可以再简化一下, @FunctionalInterface标明这个接口是一个函数式接口。函数式接口是在 JDK8才加入的,为的就是实现函数式编程,就那种Lambada表达式。

所以在 JDK1.7中,是没有@FunctionalInterface修饰的,简简单单。我们找到 JDK1.7的 Ruunable实现,是下面这样子

public interface Runnable {
    public abstract void run();
}

想了解更多函数式编程的话,可以看这篇文章:Lambda、函数式接口、Stream 一次性全给你

如果一个线程类要实现 Runnable 接口,则这个类必须定义一个名为 run 的无参数方法。

实现了 Runnable 接口的类可以通过实例化一个 Thread 实例,并将自身作为目标传递来运行。

举个例子

首先定义一个RunnableThread类,并实现自(implements)Runnable接口,然后重写 run方法。

public class RunnableThread implements Runnable{
    @Override
    public void run() {
        System.out.println("当前线程名称"+ Thread.currentThread().getName());
    }
}

使用RunnableThread作为线程类(Thread)实例化的参数,然后调用run方法。

RunnableThread runnableThread = new RunnableThread();
Thread thread = new Thread(runnableThread);
thread.start();

注意,是调用新 new 出来的 Thread 实例的start() 方法,不要调用run方法,虽然我们是重写Runnablerun方法的。调用 run方法并没有创建线程的效果,而是直接在当前线程执行,就和执行一个普通类的普通方法一模一样。

为什么要调用 start()方法呢,我们看看 Threadstart()方法实现中,其实是调用了一个名称为 start0()的 native 方法,native 方法就不是用 Java 实现的了,而是在 JVM 层面的实现。

这个start0方法的主要逻辑就是启动一个操作系统线程,并和 JVM 线程绑定,开辟一些空间来存储线程状态和上下文的数据,然后执行绑定的 JVM 线程(也就是我们实现了Runnable的类)的 run方法的代码块,从而执行我们自定义的逻辑。

还可以用线程池的方式调用

ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
        .setNameFormat("thread-pool-%d").build();
ExecutorService singleThreadPool = new ThreadPoolExecutor(1, 1,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
singleThreadPool.execute(runnableThread);
singleThreadPool.shutdown();

如果 Runnable那么完美的话,就没必要在 JDK1.5中加入和它超级相似的 Callable了。

Runnable有什么不完美的地方吗?就是它的 run方法是没有返回值的。

如果你想在主线程中拿到新开启线程的返回值的话,Runnable就不太方便了。必须要借助共享变量来完成。

所以,如果你的场景是要有返回值的话, 就要 Callable出手了。

Callable

Callable是在 JDK1.5才加入的,为的就是弥补 Runnable没有返回值的缺陷,虽然绝大多数场景都可以用 Runnable来实现。

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

Runnable类似的,@FunctionalInterface也是后来加入的,可以不考虑,只是为了函数式写法。

Callable接口只有一个 call方法,并且有一个泛型返回值,可以返回任何类型。

举个例子

首先声明一个类,实现自 Callable接口,返回值为字符串类型

public class CallableThread implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "线程名称:" + Thread.currentThread().getName();
    }
}

在代码中通过下面的方式调用

CallableThread callableThread = new CallableThread();
FutureTask<String> futureTask = new FutureTask<>(callableThread);
Thread thread = new Thread(futureTask);
thread.start();
String result = futureTask.get();
System.out.println("执行结果= " + result);

看上去就比 Runnable要复杂一点,要借助FutureTask了,因为 Thread类没有接受Callable的构造函数。

使用 FutureTask.get()方法获取执行结果。

在日常的开发中,不建议直接这样用,除非你明确的知道这样做没有问题,否则的话,推荐使用线程池的方式来使用。

CallableThread callableThread = new CallableThread();
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(callableThread);
String result = future.get();
System.out.println("任务执行结果: " + result);
executor.shutdown();

如何选择用哪一个

取舍的基本原则就是需不需要返回值,如果不需要返回值,那直接就选 Runnable,不用犹豫。如果有返回值的话,那更不用犹豫,不要想着借助共享变量的方式。

另外还有一点就是是否需要抛出异常, Runnable是不接受抛出异常的,Callable可以抛出异常。

Runnable适合那种纯异步的处理逻辑。比如每天定时计算报表,将报表存储到数据库或者其他地方,只是要计算,不需要马上展示,展示内容是在其他的方法中单独获取的。

比如那些非核心的功能,当核心流程执行完毕后,非核心功能就自己去执行吧,至于成不成功的,不是特别重要。例如一个购物下单流程,下单、减库存、加到用户的订单列表、扣款是核心功能,之后的发送APP通知、短信通知这些就启动新线程去干去吧。

最后

Runnablejava.lang这个包下,而当JDK1.5发布的时候,新加入的 Callable被安置在了 java.util.concurrent这个包下,这是 Java 里有名的并发编程相关包,各种锁啊、多线程工具类啊,都被放在这个包下。按道理,Runnable 也应该在这里才对。

可见再厉害的项目也是随着项目的扩大而慢慢的规划,而前期的一些看似不太合理的地方,只能做兼容和妥协。

到此这篇关于Java中Runnable和Callable分别什么时候使用的文章就介绍到这了,更多相关Java Runnable Callable内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot中使用EasyExcel并行导出多个excel文件并压缩zip后下载的代码详解

    SpringBoot中使用EasyExcel并行导出多个excel文件并压缩zip后下载的代码详解

    SpringBoot的同步导出方式中,服务器会阻塞直到Excel文件生成完毕,在处理大量数据的导出功能,本文给大家介绍了SpringBoot中使用EasyExcel并行导出多个excel文件并压缩zip后下载,需要的朋友可以参考下
    2024-09-09
  • Eclipse+Webservice简单开发实例

    Eclipse+Webservice简单开发实例

    这篇文章主要介绍了Eclipse+Webservice简单开发实例的相关资料,需要的朋友可以参考下
    2016-02-02
  • 简单了解Spring Web相关模块运行原理

    简单了解Spring Web相关模块运行原理

    这篇文章主要介绍了简单了解Spring Web相关模块运行原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • Spring Boot静态资源路径的配置与修改详解

    Spring Boot静态资源路径的配置与修改详解

    最近在做SpringBoot项目的时候遇到了“白页”问题,通过查资料对SpringBoot访问静态资源做了总结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-09-09
  • Java动态设置注解值及原理详解

    Java动态设置注解值及原理详解

    这篇文章主要介绍了Java动态设置注解值及原理详解,AnnotationInvocationHandler是注解的代理hander,通过反射获取类的注解时会通过AnnotationInvocationHandler创建代理对象并将数据存储到memberValues里,需要的朋友可以参考下
    2023-11-11
  • SpringBoot+MyBatisPlus+MySQL8实现树形结构查询

    SpringBoot+MyBatisPlus+MySQL8实现树形结构查询

    这篇文章主要为大家详细介绍了SpringBoot+MyBatisPlus+MySQL8实现树形结构查询,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • Java实现手机号码归属地查询

    Java实现手机号码归属地查询

    这篇文章主要为大家详细介绍了如何利用Java实现手机号码归属地查询功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-12-12
  • 用Java轻松读取Word文档内容的常用方法

    用Java轻松读取Word文档内容的常用方法

    这篇文章主要介绍了用Java轻松读取Word文档内容的常用方法,对于doc格式使用Apache POI库中的HWPFDocument和WordExtractor类,对于docx格式使用XWPFDocument类,并通过遍历段落和文本运行对象来提取文本内容,需要的朋友可以参考下
    2025-03-03
  • Java序列化与反序列化的实例分析讲解

    Java序列化与反序列化的实例分析讲解

    今天小编就为大家分享一篇关于Java序列化与反序列化的实例分析讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • Jmeter如何获取jtl文件中所有的请求报文详解

    Jmeter如何获取jtl文件中所有的请求报文详解

    JMeter的可以创建一个包含测试运行结果的文本文件,这些通常称为JTL文件,因为这是默认扩展名,但可以使用任何扩展名,这篇文章主要给大家介绍了关于Jmeter如何获取jtl文件中所有的请求报文的相关资料,需要的朋友可以参考下
    2021-09-09

最新评论