2.实现Runnable接口,重写run()方法

实现Runnable接口,重写run()方法
优点:
避免单继承限制:Java 中的类只能继承一个父类,但可以实现多个接口。通过实现 Runnable 接口,类依然可以继承其他类,因此不受单继承的限制。
提高代码的复用性:Runnable 是一个接口,多个线程可以共享同一个 Runnable 实现,通过不同的线程来执行相同的任务。
易于管理线程:实现 Runnable 接口的类可以直接传递给 Thread 对象的构造函数来创建线程,代码结构更清晰,线程管理更加集中。
支持多线程共享资源:使用 Runnable 实现类可以方便地在线程之间共享资源,因为 Runnable 对象本身是可以共享的。
支持线程池:使用 Runnable 接口实现任务时,可以方便地将任务提交给线程池,从而更好地管理和调度线程。
缺点:
不支持返回值:Runnable 接口的 run() 方法没有返回值(返回类型为 void),如果需要在任务完成后返回结果,通常需要使用其他方法(例如,结合 Future 或 Callable)。
无法抛出受检查异常:run() 方法是 void 类型,且不能抛出受检查异常(例如 IOException),这使得处理某些特定异常变得不如直接继承 Thread 类时方便。
需要额外的线程管理:实现 Runnable 时,开发者需要手动管理线程的生命周期(例如创建 Thread 对象并启动),而继承 Thread 类则可以直接在子类中控制线程的行为。
优点
优点1:避免单继承限制
当你使用 Thread 类直接创建线程时,Java 的类是单继承的,即一个类只能继承一个父类,这会导致一个问题:如果你已经继承了其他类(例如 Animal 类),你就不能再继承 Thread 类。为了绕过这个问题,你必须选择继承还是多线程。
而实现 Runnable 接口则解决了这个问题,因为接口是多实现的,你可以既继承某个类,又实现 Runnable 接口,进而同时获得线程的特性和其他类的功能。
package com.example.study.thread.runnable;
/**
* 创建一个实现 Runnable 接口的类
*/
class MyTask extends Animal implements Runnable {
@Override
public void run() {
// 在 run 方法中调用 Animal 类的 eat 方法
eat(Thread.currentThread().getName()); // 调用继承自 Animal 类的方法
System.out.println(Thread.currentThread().getName() + " 正在执行任务");
}
}
/**
* Animal类
*/
class Animal {
void eat(String name) {
System.out.println(name + " Animal is eating...");
}
}
class Main {
public static void main(String[] args) {
// 创建一个 Runnable 对象
MyTask task = new MyTask();
// 通过 Runnable 对象来创建线程
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
// 启动线程
thread1.start();
thread2.start();
}
}
Thread-0 Animal is eating...
Thread-1 Animal is eating...
Thread-0 正在执行任务
Thread-1 正在执行任务
Process finished with exit code 0
如果你直接继承 Thread 类并重写 run() 方法,则没有 Runnable 的这种解耦优势:
package com.example.study.thread.thread;
/**
* 创建一个继承 Thread 接口的类
* 此类因为继承了Thread,就不能再继承其他类了
*/
class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 正在执行任务");
}
}
class Main {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
thread1.start();
thread2.start();
}
}
优点2:提高代码的复用性
假设我们有一个多线程任务,多个线程需要执行相同的逻辑,例如打印数字。通过实现 Runnable 接口,我们可以让多个线程共享相同的任务代码,从而提高代码复用性。
package com.example.study.thread.runnable;
/**
* 定义一个实现 Runnable 接口的类
*/
public class PrintNumbersTask implements Runnable {
private int start;
private int end;
/**
* @param start 线程任务入参1 开始数字
* @param end 线程任务入参2 结束数字
* 构造函数,指定打印的数字范围
*/
public PrintNumbersTask(int start, int end) {
this.start = start;
this.end = end;
}
/**
* 实现 run() 方法,定义线程要执行的任务
*/