Java 中创建线程的四种方法
一、继承 Thread 类
-
声明一个继承自
java.lang.Thread的子类,并重写run()方法,在方法中实现线程要执行的代码。 -
实例化这个子类。
-
调用这个子类对象的
start()方法。
// 声明一个继承自 Thread 的子类
class MyThread extends Thread {
// 重写 run 方法
@Override
public void run() {
// 线程体
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
// 创建线程对象
MyThread t1 = new MyThread();
// 启动新线程
t1.start();
}
}
缺点:
- 由于 Java 的单继承机制,无法继承其他的类,不利于功能的扩展。
- 如果线程有执行结果是不能直接返回的。
二、实现 Runnable 接口
-
声明一个线程任务类,实现
Runnable接口,并实现run()方法,在方法中实现线程要执行的代码。 -
实例化这个线程任务类,并将这个线程任务类传入线程对象 Thread 的构造器。
-
调用线程对象的 start() 方法启动线程。
// 声明一个类,并实现 Runnable 接口
class MyRunnable implements Runnable {
// 实现 run() 方法
@Override
public void run() {
// 线程体
System.out.println("Runnable 线程启动了");
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
// 创建 MyRunnable 实例,作为线程执行体
MyRunnable mr = new MyRunnable();
// 创建线程对象,将 Runnable 实现类作为参数传入
Thread t1 = new Thread(mr);
// 启动线程,执行 run() 方法中的代码
t1.start();
}
}
优点: 任务类仅实现了
Runnable接口,可继续继承或实现其他类,扩展性强。缺点: 需要多创建一个
Runnable对象,如果线程有执行结果,不能直接返回的。
简化写法:
// 通过实现 Runnable 接口创建线程 public class ThreadDemo2_1 { public static void main(String[] args) { // 简化成 Lambda 表达式的匿名内部类写法 new Thread(()->{ // 线程体 System.out.println("Thread is running..."); }).start(); } }
三、实现 Callable 接口
-
创建任务对象:
-
声明一个类实现 Callable 接口,实现 call() 方法,封装要做的事和要返回的数据。
-
实例化 Callable 对象,并将其通过 FutureTask 的构造函数封装起来。
-
-
将线程任务对象通过 Thread 的构造函数传入。
-
调用 Thread 对象的 start() 方法启动线程。
-
线程执行完毕后,通过 FutureTask 的 get() 方法获取线程任务的执行结果。
// 声明线程任务类,实现 Callable 接口,通过泛型指定线程返回的数据类型
class MyCallable implements Callable<Integer> {
private final int number;
public MyCallable(int number) {
this.number = number;
}
// 实现 call 方法
public Integer call() {
// 线程体
return number;
}
}
public class ThreadDemo3 {
public static void main(String[] args) {
// 创建 Callable 实例,作为线程任务
MyCallable c1 = new MyCallable(114514);
// 将 Callable 实例封装为 FutureTask 线程任务对象
FutureTask<Integer> ft1 = new FutureTask<>(c1);
// 创建线程对象,将 FutureTask 线程任务对象作为参数传入
Thread t1 = new Thread(ft1);
// 启动线程,执行 call() 方法
t1.start();
try {
// 通过 FutureTask 线程任务对象的 get() 方法获取执行结果
Integer result = ft1.get();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
优点:
任务类仅实现接口,可继续继承或实现其他类,扩展性强。
可在线程执行完毕后获取线程执行结果。
四、使用线程池创建线程
线程池是一种管理线程的机制,它可以预先创建一定数量的线程,当有任务需要执行时,从线程池中获取线程,任务执行完毕后线程不会被销毁,而是返回到线程池供后续任务使用,从而减少线程创建和销毁的开销。
-
通过
Executors工具类创建线程池(也可以通过ThreadPoolExecutor直接创建,更灵活)。 -
向线程池提交任务(可以是
Runnable或Callable类型的任务)。 -
任务执行完成后,关闭线程池。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ThreadPoolDemo {
public static void main(String[] args) {
// 创建一个固定大小的线程池,包含3个线程
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 提交 Runnable 任务
executorService.submit(() -> {
// 线程体
System.out.println("Runnable 任务执行中,线程名:" + Thread.currentThread().getName());
});
// 提交 Callable 任务
Future<Integer> future = executorService.submit(() -> {
// 线程体
System.out.println("Callable 任务执行中,线程名:" + Thread.currentThread().getName());
return 100;
});
// 获取 Callable 任务的执行结果
try {
System.out.println("Callable 任务返回结果:" + future.get());
} catch (Exception e) {
e.printStackTrace();
}
// 关闭线程池
executorService.shutdown();
}
}
优点:
- 重用线程,减少线程创建和销毁的性能开销。
- 控制线程数量,避免因线程过多导致系统资源耗尽。
- 可以统一管理和监控线程,便于任务的调度和控制。
- 支持提交
Runnable和Callable类型的任务,灵活性高。缺点:
- 线程池的参数设置需要根据实际情况调整,设置不当可能影响性能。
- 若线程池使用不当,可能会导致任务堆积、死锁等问题。
总结
| 方式 | 优点 | 缺点 |
|---|---|---|
| 继承 Thread 类 | 编程比较简单,可直接用 Thread 类里的方法 | 扩展性欠佳,无法再继承其他类,也不能返回线程执行结果 |
| 实现 Runnable 接口 | 扩展性强,实现该接口时还能继承其他类 | 编程相对复杂,无法返回线程执行结果 |
| 实现 Callable 接口 | 扩展性强,实现该接口时还能继承其他类,且可获取线程执行结果 | 编程相对复杂 |
| 使用线程池创建线程 | 重用线程,减少性能开销;控制线程数量;支持多种任务类型;便于管理和监控 | 参数设置复杂;使用不当可能导致问题 |

浙公网安备 33010602011771号