在 Java 中,创建线程有多种方式,其中最常用的是通过实现 Runnable 接口或 Callable 接口。以下详细介绍如何创建线程,以及 Runnable 和 Callable 的区别。
1. 创建线程的方式
1.1 继承 Thread 类
最原始的方式:通过继承 Thread 类并重写 run() 方法来创建线程。
示例:
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread is running");
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 启动线程
}
}
1.2 实现 Runnable 接口
通过实现 Runnable 接口并重写 run() 方法来创建线程。这个方式可以通过写一个私有function成员,将其放入run进行执行,做成task的形式,部分框架会使用该方式。
示例:
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Thread is running");
}
}
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start(); // 启动线程
}
}
1.3 实现 Callable 接口
通过实现 Callable 接口并重写 call() 方法来创建线程。
Callable 可以返回结果并抛出异常。FutureTask会以该接口的实现为参数进行执行
示例:
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "Thread is running";
}
}
public class Main {
public static void main(String[] args) throws Exception {
FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
Thread thread = new Thread(futureTask);
thread.start(); // 启动线程
System.out.println(futureTask.get()); // 获取线程返回值
}
}
1.4 使用线程池
通过线程池(如 ExecutorService)管理线程。
示例:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.submit(() -> System.out.println("Thread is running"));
executor.shutdown(); // 关闭线程池
}
}
2. Runnable 和 Callable 的区别
特性 | Runnable | Callable |
---|---|---|
返回值 | 无返回值(void) | 有返回值(泛型类型) |
异常处理 | 不能抛出受检异常 | 可以抛出受检异常 |
使用场景 | 简单的异步任务 | 需要返回结果或抛出异常的任务 |
实现方法 | run() | call() |
线程启动方式 | 通过 Thread 或线程池提交 | 通过 FutureTask 或线程池提交 |
2.1 Runnable
特点:
无返回值。
不能抛出受检异常。
示例:
Runnable task = () -> System.out.println("Running task");
new Thread(task).start();
2.2 Callable
特点:
有返回值(通过 Future 获取)。
可以抛出受检异常。
示例:
Callable<String> task = () -> {
return "Task result";
};
Future<String> futureTask = new FutureTask<>(task);
new Thread(futureTask).start();
System.out.println(futureTask.get()); // 获取返回值
3. Future 和 FutureTask
Future:
只是一个接口定义,对于获取结果而言都是获取了FutureTask对象,然后get出来具体的返回值。
FutureTask:
实现了 Runnable 和 Future 接口。
可以包装 Callable 或 Runnable 任务。
示例
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class Main {
public static void main(String[] args) throws Exception {
Callable<String> task = () -> "Task result";
Future<String> futureTask = new FutureTask<>(task);
new Thread(futureTask).start();
System.out.println(futureTask.get()); // 获取返回值
}
}
4. 总结
创建线程的方式:
继承 Thread 类。
实现 Runnable 接口。
实现 Callable 接口。
使用线程池。
Runnable 和 Callable 的区别:
Runnable 无返回值,不能抛出受检异常。
Callable 有返回值,可以抛出受检异常。
适用场景:
如果任务不需要返回结果,使用 Runnable。
如果任务需要返回结果或抛出异常,使用 Callable。
通过合理选择线程创建方式和任务接口,可以更好地满足多线程编程的需求。