面试官:Java 中 runnable 和 callable 有什么区别?并用代码具体举例说明
共 8420字,需浏览 17分钟
·
2024-06-18 12:02
Java中的Runnable
和Callable
接口都是为了实现多线程而设计的,但它们之间存在一些关键差异:
主要区别:
-
返回值:
-
Runnable
接口的run()
方法没有返回值(返回类型为void
),适用于不需要线程返回结果的情景。 -
Callable
接口的call()
方法可以有返回值(通过泛型指定返回类型),并且该方法可以抛出异常,这使得它更强大,适合需要线程执行后返回结果的场景。 -
异常处理:
-
Runnable
的run()
方法不能声明抛出检查异常(checked exceptions),因此处理异常需要在方法内部进行。 -
Callable
的call()
方法可以声明抛出异常,包括检查异常,使得异常处理更加灵活和规范。
代码示例:
Runnable 示例:
class PrintTask implements Runnable {
private String message;
public PrintTask(String message) {
this.message = message;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ": " + message);
}
}
public class RunnableExample {
public static void main(String[] args) {
Thread thread = new Thread(new PrintTask("Hello from Runnable"));
thread.start();
}
}
Callable 示例:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class ComputeTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
return sum;
}
}
public class CallableExample {
public static void main(String[] args) {
ComputeTask task = new ComputeTask();
FutureTask<Integer> futureTask = new FutureTask<>(task);
Thread thread = new Thread(futureTask);
thread.start();
try {
Integer result = futureTask.get(); // 获取Callable线程的返回值
System.out.println("Sum: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
在CallableExample
中,我们创建了一个实现了Callable<Integer>
接口的ComputeTask
类,它计算0到99的和,并通过FutureTask
包装后启动线程。主线程通过futureTask.get()
方法获取到了Callable
线程的执行结果。而在RunnableExample
中,PrintTask
只是简单打印一条消息,没有返回值。
Java实现多线程有多种方式,下面我会列举几种主要的实现方式并提供相应的代码示例。
1. 继承 Thread
类
创建一个新的类继承自 Thread
类,并重写 run
方法来定义线程执行的代码。
class MyThread extends Thread {
@Override
public void run() {
System.out.println("继承Thread类方式运行");
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start(); // 启动线程
}
}
2. 实现 Runnable
接口
定义一个类实现 Runnable
接口,并实现 run
方法。然后创建 Thread
对象,将实现了 Runnable
的对象作为参数传递给 Thread
构造器。
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("实现Runnable接口方式运行");
}
}
public class RunnableExample {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
}
}
3. 实现 Callable
接口(通过 FutureTask
包装)
Callable
接口类似于 Runnable
,但是它可以返回结果,并且可以抛出异常。需要通过 FutureTask
封装后,再作为 Thread
的目标。
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "Callable方式运行";
}
}
public class CallableExample {
public static void main(String[] args) {
MyCallable myCallable = new MyCallable();
FutureTask<String> futureTask = new FutureTask<>(myCallable);
Thread t = new Thread(futureTask);
t.start();
try {
String result = futureTask.get(); // 获取返回值
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
4. 使用线程池(ExecutorService
)
通过 ExecutorService
提供的线程池来管理和控制线程,可以有效复用线程,减少资源开销。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
class Task implements Runnable {
@Override
public void run() {
System.out.println("通过线程池运行");
}
}
public class ThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2); // 创建固定大小线程池
executor.submit(new Task());
executor.shutdown(); // 关闭线程池
try {
executor.awaitTermination(5, TimeUnit.SECONDS); // 等待所有任务完成
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
以上是Java中实现多线程的几种主要方式,每种方式都有其适用场景,开发者可以根据实际需求选择最合适的实现方式。
欢迎添加程序汪个人微信 itwang008 进粉丝群或围观朋友圈
评论