在Java中,多线程是实现并发编程的核心技术,能够充分利用CPU资源,提高程序执行效率,创建多线程的方式主要有四种,每种方式都有其特点和适用场景,本文将详细介绍这些方法的实现原理、优缺点及使用注意事项。

继承Thread类创建线程
继承Thread类是最基础的创建线程方式,只需自定义一个类继承Thread类,并重写其run()方法即可。run()方法是线程的执行体,包含了线程要完成的任务逻辑,创建线程后,通过调用start()方法启动线程,JVM会自动调用run()方法。
示例代码:
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
thread1.start(); // 启动线程1
thread2.start(); // 启动线程2
}
}
优点:实现简单,直接继承Thread类即可获取线程相关方法。
缺点:Java单继承的限制,导致自定义线程类无法再继承其他父类;线程与任务逻辑耦合,不利于代码复用。
实现Runnable接口创建线程
为了避免单继承的局限性,Java推荐通过实现Runnable接口创建线程,自定义类实现Runnable接口并重写run()方法,然后将该实例作为参数传递给Thread类的构造器,最后调用start()方法启动线程。
示例代码:
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
public class RunnableDemo {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
thread1.start();
thread2.start();
}
}
优点:突破了单继承的限制,线程任务与线程本身分离,便于多个线程共享同一任务逻辑;符合“面向接口编程”的思想,代码扩展性更好。
缺点:需要额外创建Thread对象,线程的状态管理(如获取当前线程)仍需依赖Thread类。

实现Callable接口与Future接口创建线程
Runnable接口的run()方法没有返回值,也无法抛出受检异常,若需要线程返回结果或处理异常,可通过实现Callable接口结合Future接口实现。Callable接口的call()方法支持返回值和异常抛出,而Future接口用于获取线程执行结果。
实现步骤:
- 自定义类实现
Callable<V>接口(V为返回值类型),重写call()方法; - 通过
ExecutorService线程池执行Callable任务,返回Future<V>对象; - 调用
Future.get()方法获取线程执行结果(该方法会阻塞,直到线程完成)。
示例代码:
import java.util.concurrent.*;
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 5; i++) {
sum += i;
Thread.sleep(100);
}
return sum;
}
}
public class CallableDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(new MyCallable());
System.out.println("线程执行结果: " + future.get());
executor.shutdown();
}
}
优点:支持返回值和异常处理,功能更强大;通过线程池管理线程,避免频繁创建和销毁线程的开销。
缺点:代码相对复杂,需要依赖线程池;Future.get()会阻塞主线程,可能影响并发性能。
使用线程池创建线程
直接创建线程(new Thread)会导致线程数量不可控,频繁创建和销毁线程会消耗大量系统资源,线程池通过复用已创建的线程,控制并发线程数量,提高程序稳定性,Java提供了ExecutorService接口和Executors工具类来管理线程池。
常用线程池类型:

- newFixedThreadPool:固定大小线程池,线程数量固定,超出任务进入队列等待。
- newCachedThreadPool:可缓存线程池,线程数量根据任务动态调整,空闲线程会被回收。
- newSingleThreadExecutor:单线程线程池,所有任务按顺序执行。
- newScheduledThreadPool:定时任务线程池,支持延迟或周期性执行任务。
示例代码(固定大小线程池):
import java.util.concurrent.*;
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
executor.execute(() -> {
System.out.println(Thread.currentThread().getName() + " is running");
});
}
executor.shutdown(); // 关闭线程池
}
}
优点:控制线程数量,避免资源耗尽;复用线程,降低创建/销毁开销;提供任务队列和拒绝策略,提升系统稳定性。
缺点:线程池参数配置不当可能导致性能问题(如线程数过多或过少);任务队列积满可能引发内存溢出。
多线程创建方式的对比与选择
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 继承Thread类 | 实现简单,直接调用线程方法 | 单继承限制,线程与任务耦合 | 简单并发任务,无需复用任务逻辑 |
| 实现Runnable接口 | 突破单继承限制,任务与线程分离 | 需额外创建Thread对象,无法直接返回结果 | 多线程共享任务逻辑,通用并发场景 |
| 实现Callable接口 | 支持返回值和异常处理,功能强大 | 代码复杂,依赖线程池 | 需要线程返回结果或处理异常的任务 |
| 线程池 | 控制线程数量,复用线程,稳定性高 | 参数配置复杂,任务队列可能积满 | 高并发场景,如Web服务器、数据处理任务 |
多线程使用的注意事项
- 避免线程安全问题:多个线程共享数据时,需使用
synchronized、Lock或volatile等机制保证数据一致性。 - 合理设置线程优先级:线程优先级仅作为调度参考,不能保证执行顺序,避免过度依赖优先级。
- 防止线程死锁:避免多个线程因竞争资源而相互等待,可通过破坏“请求与保持”条件预防。
- 及时释放资源:使用线程池后需调用
shutdown()或shutdownNow()关闭线程池,避免资源泄漏。
Java创建多线程的方式各有优劣,开发者需根据实际场景选择合适的方法,对于简单任务,可选择继承Thread类或实现Runnable接口;对于需要返回值的复杂任务,Callable接口与线程池是更好的选择;在高并发场景下,线程池能显著提升系统性能和稳定性,无论采用哪种方式,都需要关注线程安全、资源管理等问题,确保程序高效、可靠地运行。




















