-
定义和返回值的区别
-
Runnable 接口:
- 定义在
java.lang包中,是一个函数式接口,只包含一个抽象方法run。这个方法没有返回值,签名为public void run()。它主要用于定义一个任务单元,这个任务单元在被线程执行时,只是单纯地执行一段代码逻辑,不需要返回结果给调用者。例如,一个简单的打印任务可以这样定义:
- 定义在
-
class PrintTask implements Runnable {
@Override
public void run() {
System.out.println("执行打印任务");
}
}
-
Callable 接口:
- 定义在
java.util.concurrent包中,同样是函数式接口,包含一个抽象方法call。call方法有返回值,并且可以抛出异常,其签名为V call() throws Exception(V是返回值类型,由实现类确定)。这意味着使用Callable定义的任务可以在执行完成后返回一个结果给调用者,适用于那些需要获取异步任务执行结果的场景。例如,一个计算任务可以这样定义:
- 定义在
import java.util.concurrent.Callable;
class CalculateTask implements Callable<Integer> {
@Override
public Integer call() {
return 2 + 3;
}
}
-
用法区别
-
Runnable 的用法:
- 创建线程执行任务:要执行
Runnable任务,通常需要将Runnable实现类的实例传递给Thread类的构造函数,然后调用Thread的start方法来启动线程。例如:
- 创建线程执行任务:要执行
-
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("这是一个使用Runnable的任务");
}
}
public class Main {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
}
- 在线程池中的应用:在
Executor框架的线程池中,Runnable任务可以通过ExecutorService的execute方法提交。例如:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class AnotherRunnable implements Runnable {
@Override
public void run() {
System.out.println("在线程池中执行的Runnable任务");
}
}
public class Main {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
AnotherRunnable anotherRunnable = new AnotherRunnable();
executorService.execute(anotherRunnable);
executorService.shutdown();
}
}
-
Callable 的用法:
- 结合 Future 获取结果:因为
Callable有返回值,所以在使用时通常需要和Future或FutureTask一起配合。Future用于表示异步任务的结果,它提供了方法来检查任务是否完成、获取任务结果等。FutureTask是Future的一个实现,它同时实现了Runnable接口,所以可以将Callable实例包装到FutureTask中,再交给Thread启动或者提交给线程池。例如:
- 结合 Future 获取结果:因为
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class MyCallable implements Callable<String> {
@Override
public String call() {
return "这是Callable返回的结果";
}
}
public class Main {
public static void main(String[] args) {
MyCallable myCallable = new MyCallable();
FutureTask<String> futureTask = new FutureTask<>(myCallable);
Thread thread = new Thread(futureTask);
thread.start();
try {
System.out.println(futureTask.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
- 在线程池中的应用(与 Runnable 类似但有区别) :在
Executor框架的线程池中,Callable任务通过ExecutorService的submit方法提交,这个方法会返回一个Future对象用于获取结果。例如:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
class CalculateCallable implements Callable<Integer> {
@Override
public Integer call() {
return 5 * 6;
}
}
public class Main {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
CalculateCallable calculateCallable = new CalculateCallable();
Future<Integer> future = executorService.submit(calculateCallable);
try {
System.out.println("计算结果为:" + future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
executorService.shutdown();
}
}
-
适用场景的区别
-
Runnable 适用场景:
- 简单的任务执行:当只需要执行一段简单的代码逻辑,不需要返回结果给调用者时,如打印日志、发送简单的通知等任务,使用
Runnable非常合适。 - 资源消耗型任务的异步执行:对于一些资源消耗型任务,如文件读取、网络请求等,将它们放在单独的线程中以
Runnable任务的形式执行,可以避免阻塞主线程,提高程序的响应速度。
- 简单的任务执行:当只需要执行一段简单的代码逻辑,不需要返回结果给调用者时,如打印日志、发送简单的通知等任务,使用
-
Callable 适用场景:
- 需要获取任务结果的计算任务:在分布式计算、数据处理等场景中,当一个任务需要返回计算结果,例如对大数据集进行复杂的数学计算、从数据库中获取并处理数据后返回结果等,
Callable接口是更好的选择。 - 异步任务链:在一些复杂的异步任务处理中,一个任务的结果可能是下一个任务的输入,使用
Callable可以方便地获取每个任务的结果,构建异步任务链。例如,先从数据库获取用户信息,然后根据用户信息进行复杂的业务逻辑计算,最后返回最终结果,就可以通过多个Callable任务和Future来实现。
- 需要获取任务结果的计算任务:在分布式计算、数据处理等场景中,当一个任务需要返回计算结果,例如对大数据集进行复杂的数学计算、从数据库中获取并处理数据后返回结果等,
-