多线程的三种实现方法
方式1:继承Thread类
代码特点:
class Mythread extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName() + "hello world");}}
}// 使用
public class ThreadDemo {public static void main(String[] args) {Mythread t1=new Mythread();Mythread t2=new Mythread();t1.setName("线程1");t2.setName("线程2");t1.start();t2.start();}
}
优点:
- ✅ 简单直接:继承Thread类,重写run方法即可
- ✅ 代码清晰:线程逻辑和线程对象在一起
缺点:
- ❌ 单继承限制:Java只能单继承,限制了类的扩展性
- ❌ 耦合度高:任务和线程绑定在一起
- ❌ 资源浪费:每个任务都需要创建新的Thread对象
适用场景:
- 简单的线程测试
- 学习多线程基础概念
- 不需要继承其他类的场景
方式2:实现Runnable接口
代码特点:
/*
* 多线程第二种启动方式
* 1.自己定义一个类实现Runnable接口
* 2.重写里面的run方法
* 3.创建自己类的对象
* 4.创建一个Thread类对象,并开启线程
* */
class Myrun implements Runnable {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName());}}
}public class ThreadDemo {public static void main(String[] args) {/** 多线程第二种启动方式* 1.自己定义一个类实现Runnable接口* 2.重写里面的run方法* 3.创建自己类的对象* 4.创建一个Thread类对象,并开启线程* *///创建Myrun的对象//表示多线程要执行的任务Myrun mr=new Myrun();//创建线程对象Thread t1=new Thread(mr);Thread t2=new Thread(mr);//给线程设置名字t1.setName("线程1");t2.setName("线程2");t1.start();t2.start();}
}
优点:
- ✅ 灵活性强:可以继承其他类,实现多个接口
- ✅ 资源共享:多个线程可以共享同一个Runnable对象
- ✅ 符合面向接口编程:遵循"面向接口编程"原则
- ✅ 支持Lambda表达式:代码更简洁
缺点:
- ❌ 代码稍复杂:需要创建Thread对象
- ❌ 无法直接获取线程信息:需要通过Thread.currentThread()
适用场景:
- 日常开发中的多线程任务
- 需要资源共享的场景
- 符合面向对象设计原则的项目
方式3:实现Callable接口
代码特点:
/* * 多线程的第三种实现方法 * 1.创建一个类MyCallable实现Callable接口 * 2.重写call(是有返回值的,表示多线程的运行结果) * 3.创建MyCallable对象(表示多线程要执行的任务) * 4..创建FutureTask的对象(作用管理多线程运行的结果) * 5.创建Thread类的对象,并启动(表示线程) * * */
public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {//求1-100的和int sum=0;for (int i = 0; i <= 100; i++) {sum+=i;}return sum;}
}
public class ThreadDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {MyCallable mc=new MyCallable();FutureTask<Integer> futureTask=new FutureTask<>(mc);Thread t1=new Thread(futureTask);t1.start();//获取多线程运行的结果Integer result=futureTask.get();System.out.println(result);}
}
优点:
- ✅ 可以返回结果:call方法有返回值
- ✅ 可以抛出异常:支持异常处理
- ✅ 支持泛型:返回值类型灵活
- ✅ 异步获取结果:通过FutureTask异步获取结果
缺点:
- ❌ 使用复杂:需要FutureTask包装
- ❌ 代码冗长:创建和使用过程较复杂
- ❌ 阻塞获取:get()方法会阻塞等待结果
适用场景:
- 需要返回值的计算任务
- 需要异常处理的场景
- 异步计算结果的获取
特性 | 继承Thread | 实现Runnable | 实现Callable |
---|---|---|---|
复杂度 | 最简单 | 简单 | 较复杂 |
继承限制 | 只能单继承 | 可以继承其他类 | 可以继承其他类 |
返回值 | 无 | 无 | 有 |
异常处理 | 不能抛出 | 不能抛出 | 可以抛出 |
资源共享 | 不支持 | 支持 | 支持 |
推荐程度 | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
实际应用建议
1. 学习阶段:从继承Thread开始
- 理解多线程基本概念
- 掌握线程创建和启动
2. 日常开发:优先使用Runnable
- 灵活性强,符合设计原则
- 支持资源共享,效率高
3. 特殊需求:选择Callable
- 需要返回值的计算任务
- 需要异常处理的场景
总结:Runnable是最推荐的方式,既灵活又高效,适合大部分开发场景!