概要
当使用Java线程时,我们通常有两种主要的方式来创建线程:继承Thread类或者实现Runnable接口。
一、 继承Thread类
示例:
public class MyThread extends Thread {
// 重写Thread类的run方法
@Override
public void run() {
// 线程执行的代码
for (int i = 0; i < 10; i++) {
System.out.println("线程 " + Thread.currentThread().getId() + " 执行: " + i);
try {
// 模拟耗时操作
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// 创建线程对象
MyThread thread = new MyThread();
// 启动线程
thread.start();
// 在主线程中执行一些操作
for (int i = 0; i < 10; i++) {
System.out.println("主线程 " + Thread.currentThread().getId() + " 执行: " + i);
try {
// 模拟耗时操作
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
解释及注意事项:
- MyThread类继承了Thread类,并重写了run方法。run方法是线程执行体,包含了线程要执行的代码。
- 在main方法中,我们创建了一个MyThread对象,并调用了它的start方法。start方法会启动一个新线程,并执行run方法中的代码。
- 注意,我们不能直接调用run方法,因为那样会在当前线程(即主线程)中执行run方法中的代码,而不是在新的线程中。
- 在main方法中,我们还添加了一个简单的循环来模拟主线程的执行。
二、 实现Runnable接口
示例:
public class MyRunnable implements Runnable {
@Override
public void run() {
// 线程执行的代码
for (int i = 0; i < 10; i++) {
System.out.println("线程 " + Thread.currentThread().getId() + " 执行: " + i);
try {
// 模拟耗时操作
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// 创建Runnable对象
MyRunnable runnable = new MyRunnable();
// 创建Thread对象,并将Runnable对象作为参数传入
Thread thread = new Thread(runnable);
// 启动线程
thread.start();
// 在主线程中执行一些操作
for (int i = 0; i < 10; i++) {
System.out.println("主线程 " + Thread.currentThread().getId() + " 执行: " + i);
try {
// 模拟耗时操作
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
解释:
- MyRunnable类实现了Runnable接口,并重写了run方法。与继承Thread类不同,这里我们并没有继承Thread类,而是实现了一个接口。
- 在main方法中,我们首先创建了一个MyRunnable对象,然后将其作为参数传递给Thread类的构造函数,创建了一个Thread对象。接着,我们调用这个Thread对象的start方法来启动线程。
- 其余的代码与继承Thread类的示例类似。
三、为什么通常推荐使用实现Runnable接口的方式?
- Java不支持多重继承,但可以实现多个接口。如果一个类已经继承了其他类,但又需要具有多线程能力,那么只能实现Runnable接口。
- 在Java中,线程是宝贵的系统资源,不应该频繁地创建和销毁。通过实现Runnable接口,我们可以将线程和任务(即Runnable对象)分离,这样我们可以将多个任务分配给同一个线程,从而实现线程的复用。
- 相对于继承Thread类,实现Runnable接口的方式更加符合面向对象的设计原则,因为它将线程(Thread)和任务(Runnable)解耦了。