在Java中,创建线程主要有两种方式:继承Thread
类和实现Runnable
接口。这两种方式各有特点,适用于不同的场景。下面将详细阐述这两种方式及其区别。
一、继承Thread
类
1. 基本步骤
通过继承Thread
类来创建线程是最基本的一种方式。具体步骤如下:
- 定义
Thread
的子类:通过继承java.lang.Thread
类来定义一个子类。 - 重写
run
方法:在子类中重写Thread
类的run
方法,这个run
方法的方法体就代表了线程需要执行的任务。 - 创建线程对象:通过
new
关键字创建子类的实例,这个实例就是线程对象。 - 启动线程:调用线程对象的
start
方法来启动线程,start
方法会调用run
方法,但并不会直接执行run
方法中的代码,而是让线程去执行。
示例代码
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程运行中...");
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start(); // 启动线程
}
}
2. 优缺点
优点:
- 实现简单,容易理解,特别是对于初学者来说,这种方式比较直观。
缺点:
- Java不支持多重继承,因此如果继承了
Thread
类,那么这个类就不能再继承其他的类了。这在一些需要继承其他类以获取特定功能的场景中会受到限制。 - 线程类只是代表了线程需要执行的任务,但是线程和任务的执行代码耦合在一起,不利于代码的复用。
二、实现Runnable
接口
1. 基本步骤
通过实现Runnable
接口来创建线程是另一种常用的方式。具体步骤如下:
- 定义
Runnable
接口的实现类:实现java.lang.Runnable
接口,并重写其run
方法。 - 创建
Runnable
实现类的实例:通过new
关键字创建Runnable
实现类的实例。 - 创建
Thread
对象并传入Runnable
实例:将Runnable
实现类的实例作为构造参数传递给Thread
类的构造方法,从而创建Thread
对象。 - 启动线程:调用
Thread
对象的start
方法来启动线程。
示例代码
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程运行中...");
}
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start(); // 启动线程
}
}
2. 优缺点
优点:
- 实现了
Runnable
接口的类还可以继承其他的类,这解决了Java单继承的限制。 - 任务和线程的分离使得代码更加灵活,
Runnable
接口的实现类可以代表一个任务,而线程对象则用于执行这个任务,这样有利于代码的复用。 - 在多线程环境下,多个线程可以共享同一个
Runnable
实现类的实例,这有利于线程间的通信和资源共享。
缺点:
- 相对于继承
Thread
类来说,实现Runnable
接口的方式稍微复杂一些,需要多创建一个Thread
对象。
三、两种方式的区别
继承Thread 类 | 实现Runnable 接口 | |
---|---|---|
基本思想 | 通过扩展Thread 类来创建线程 | 通过实现Runnable 接口来创建线程 |
继承/实现 | 继承Thread 类 | 实现Runnable 接口 |
构造器 | 使用Thread 类的构造器 | 使用Thread 类的构造器,但构造器参数是Runnable 接口的实现类实例 |
代码复用 | 线程和任务代码耦合在一起,复用性较差 | 线程和任务分离,复用性较好 |
Java单继承限制 | 继承Thread 类后不能再继承其他类 | 实现了Runnable 接口的类还可以继承其他类 |
资源共享 | 多个线程不能共享同一个Thread 类的实例(因为实例代表线程本身) | 多个线程可以共享同一个Runnable 接口的实现类实例,有利于线程间的通信和资源共享 |
使用场景 | 简单场景,不需要继承其他类时 | 需要继承其他类,或者需要更高的代码复用性和灵活性 |