Runnable和Callable啥时候用?

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

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

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

java编程时,有时会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();
}

如果一个线程类要实现 Runnable 接口,则这个类必须定义一个名为 run 的无参数方法。实现了 Runnable 接口的类可以通过实例化一个 Thread 实例,并将自身作为目标传递来运行。

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

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

启动代码如下:

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

Callable

Callable是在 JDK1.5才加入的,弥补 Runnable没有返回值的缺陷。

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

和 Runnable类似的,@FunctionalInterface也是后来加入的,可以不考虑,只是为了函数式写法。Callable接口只有一个 call方法,并且有一个泛型返回值,可以返回任何类型。

// Callable示例
public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("This is a Callable task.");
        return 123; // 返回一个整数
    }
}

调用代码如下:

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(new MyCallable());
Integer result = future.get(); // 获取任务执行的结果

RunnableCallable的区别

RunnableCallable都是Java中用于多线程编程的接口,它们之间的主要区别在于:

  1. 返回值:Runnablerun()方法没有返回值,而Callablecall()方法有一个泛型类型的返回值。

  2. 异常抛出:Runnablerun()方法不能抛出异常,而Callablecall()方法可以抛出异常,需要使用try-catch语句或者将异常向上抛出。

  3. 使用方式:Runnable通常作为启动线程的参数使用,而Callable通常作为提交任务到线程池中的参数使用,可以获取任务执行的结果。

RunnableCallable使用注意事项

在使用RunnableCallable时,需要注意以下几个事项:

  1. 返回值处理:Runnablerun()方法没有返回值,而Callablecall()方法有一个泛型类型的返回值。对于Callable任务,可以通过Future对象获取任务执行的结果。在获取结果时,可以选择等待任务执行完成(调用future.get())或者设置超时时间(调用future.get(timeout, unit))。

  2. 异常处理:Runnablerun()方法不能抛出异常,而Callablecall()方法可以抛出异常。在使用Callable时,需要注意捕获可能抛出的异常,并进行适当的处理。

  3. 线程池使用:通常情况下,Runnable任务适合使用Thread启动,而Callable任务适合使用线程池提交。线程池能够更好地管理和复用线程资源,提高程序的性能和效率。

  4. 任务取消:可以通过Future对象的cancel()方法取消正在执行的任务。但是需要注意,如果任务已经开始执行,可能无法立即中止。此外,如果任务已经完成或取消,再次调用cancel()方法将返回false

  5. 并发安全:在多线程环境下使用RunnableCallable时,需要注意数据的并发安全性。确保共享数据访问的正确性,可以采用同步机制(如synchronized关键字、Lock接口)或使用并发容器(如ConcurrentHashMapCopyOnWriteArrayList等)来保证线程安全。

  6. 线程间通信:RunnableCallable任务之间可以通过共享变量或者线程间通信机制进行交互。例如,可以使用wait()notifyAll()方法实现等待和唤醒的机制。

RunnableCallable如何取舍

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

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

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

更多消息资讯,请访问****昂焱数据****https://www.ayshuju.com

相关推荐
P.H. Infinity8 分钟前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天12 分钟前
java的threadlocal为何内存泄漏
java
caridle23 分钟前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
萧鼎26 分钟前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步
学地理的小胖砸27 分钟前
【一些关于Python的信息和帮助】
开发语言·python
疯一样的码农27 分钟前
Python 继承、多态、封装、抽象
开发语言·python
^velpro^28 分钟前
数据库连接池的创建
java·开发语言·数据库
苹果醋332 分钟前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
秋の花36 分钟前
【JAVA基础】Java集合基础
java·开发语言·windows
小松学前端39 分钟前
第六章 7.0 LinkList
java·开发语言·网络