一、两种方式代码实现演示
方式 1:继承 Thread 类(extends Thread)
//1.自定义类继承Thread,重写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 ThreadTest{ public static void main(String[] args) { MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); t1.start();//开启新线程 t2.start(); } }方式 2:实现 Runnable 接口(implements Runnable)
//1.任务类实现Runnable,重写run() class MyRunnable implements Runnable{ @Override public void run() { for(int i=0;i<5;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } } //测试 public class RunnableTest{ public static void main(String[] args) { MyRunnable task = new MyRunnable();//同一个任务对象 Thread t1 = new Thread(task,"线程A"); Thread t2 = new Thread(task,"线程B"); t1.start(); t2.start(); } }二、核心区别对比
| 对比维度 | 继承 Thread 类 | 实现 Runnable 接口 |
| 继承限制 | Java 单继承,继承 Thread 后不能再继承其他父类,扩展性差 | 实现接口,还可以继承别的类、实现多个接口,规避单继承局限 |
| 资源共享 | 每个线程都是独立对象,无法共用任务数据 | 同一个 Runnable 实例可传入多个 Thread,多线程共享任务资源(适合卖票、计数场景) |
| 任务与线程耦合 | 任务代码绑定在线程类中,线程和任务耦合在一起 | 任务和线程分离:Runnable 存业务任务,Thread 只做线程调度,解耦设计 |
| 代码复用 | 复用性差,任务和线程绑定 | 任务可复用,被多个线程、线程池调用 |
三、关键场景举例:多线程共享资源(卖票案例)
1. 继承 Thread:无法共享票数(每个对象独立数据)
class TicketThread extends Thread{ private int ticket=10;//每个实例独立10张票 @Override public void run(){ while(ticket>0){ System.out.println(Thread.getName()+"出票:"+ticket--); } } } //三个线程各自10张票,总共卖出30张,不符合需求 new TicketThread().start(); new TicketThread().start(); new TicketThread().start();2. 实现 Runnable:共享同一份票数(正确卖票)
class TicketRun implements Runnable{ private int ticket=10;//唯一实例,多线程共享 @Override public void run(){ while(ticket>0){ System.out.println(Thread.currentThread().getName()+"出票:"+ticket--); } } } //同一个任务对象,3个线程争抢10张票 TicketRun task = new TicketRun(); new Thread(task,"窗口1").start(); new Thread(task,"窗口2").start(); new Thread(task,"窗口3").start();四、选择
1、优先选择:实现 Runnable 接口
规避 Java 单继承限制,面向接口编程;
天然支持多线程共享数据,适配线程池、任务调度;
任务与线程解耦,符合单一职责。
2、仅简单场景使用:继承 Thread
线程任务简单、不需要共享资源、无需拓展继承时,代码书写快捷。