提到 Java 就不得不说多线程了,就算你不想说,面试官也得让你说呀,对不对。那说到多线程,就不得提线程了(这不废话吗)。那说到线程,就不得不说Runnable
和Callable
这两个家伙了。
说熟悉也是真熟悉,在刚学习多线程的时候,第一个例子大概就是下面这样子的。
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(); // 获取任务执行的结果
Runnable
和Callable的区别
Runnable
和Callable
都是Java中用于多线程编程的接口,它们之间的主要区别在于:
-
返回值:
Runnable
的run()
方法没有返回值,而Callable
的call()
方法有一个泛型类型的返回值。 -
异常抛出:
Runnable
的run()
方法不能抛出异常,而Callable
的call()
方法可以抛出异常,需要使用try-catch
语句或者将异常向上抛出。 -
使用方式:
Runnable
通常作为启动线程的参数使用,而Callable
通常作为提交任务到线程池中的参数使用,可以获取任务执行的结果。
Runnable
和Callable使用注意事项
在使用Runnable
和Callable
时,需要注意以下几个事项:
-
返回值处理:
Runnable
的run()
方法没有返回值,而Callable
的call()
方法有一个泛型类型的返回值。对于Callable
任务,可以通过Future
对象获取任务执行的结果。在获取结果时,可以选择等待任务执行完成(调用future.get()
)或者设置超时时间(调用future.get(timeout, unit)
)。 -
异常处理:
Runnable
的run()
方法不能抛出异常,而Callable
的call()
方法可以抛出异常。在使用Callable
时,需要注意捕获可能抛出的异常,并进行适当的处理。 -
线程池使用:通常情况下,
Runnable
任务适合使用Thread
启动,而Callable
任务适合使用线程池提交。线程池能够更好地管理和复用线程资源,提高程序的性能和效率。 -
任务取消:可以通过
Future
对象的cancel()
方法取消正在执行的任务。但是需要注意,如果任务已经开始执行,可能无法立即中止。此外,如果任务已经完成或取消,再次调用cancel()
方法将返回false
。 -
并发安全:在多线程环境下使用
Runnable
和Callable
时,需要注意数据的并发安全性。确保共享数据访问的正确性,可以采用同步机制(如synchronized
关键字、Lock
接口)或使用并发容器(如ConcurrentHashMap
、CopyOnWriteArrayList
等)来保证线程安全。 -
线程间通信:
Runnable
和Callable
任务之间可以通过共享变量或者线程间通信机制进行交互。例如,可以使用wait()
和notifyAll()
方法实现等待和唤醒的机制。
Runnable
和Callable
如何取舍
取舍的基本原则就是需不需要返回值,如果不需要返回值,那直接就选 Runnable,不用犹豫。如果有返回值的话,那更不用犹豫,不要想着借助共享变量的方式。 Runnable是不接受抛出异常的,Callable可以抛出异常。
Runnable适合那种纯异步的处理逻辑。比如每天定时计算报表,将报表存储到数据库或者其他地方,只是要计算,不需要马上展示,展示内容是在其他的方法中单独获取的。
比如那些非核心的功能,当核心流程执行完毕后,非核心功能就自己去执行吧,至于成不成功的,不是特别重要。例如一个购物下单流程,下单、减库存、加到用户的订单列表、扣款是核心功能,之后的发送APP通知、短信通知这些就启动新线程去干去吧。
更多消息资讯,请访问****昂焱数据****(https://www.ayshuju.com)