一、基本概念
开发程序是为了解决问题
1.程序
一个存在磁盘中的程序(一份文件 代码文件+数据文件)不能解决问题
2.进程
正在运行中的程序 代码和数据 都在内存中
- 可以解决问题:通过(代码-计算机指令)调度计算机资源(CPU缓存、RAM磁盘、IOSystem IO操作系统)来解决问题
- 当一个计算机需要同时运行多个进程时,需要操作系统来帮忙
- 当一个程序需要同时进行多个任务时,需要使用线程
- 操作系统分配资源的最小单元
3.线程
- 一个进程的组成部分,任何一个进程都至少有一个线程
- 进程也需要操作系统帮忙管理线程
- CPU分配执行任务的最小单元 就是线程
- 进程使用多线程的优势是:可以共享数据
二、线程
1.Java中实现线程的方式
(1)继承父类(Thread)
- 继承:public class MyThread extends Thread{ }
- 重写方法run: public void run( ){ }
- 将需要独立放在线程中执行的代码写在run方法中
- 启动线程:使用MyThread类 创建对象并且调用start方法(不可调用run)
eg.创建继承 Thread 的类
/** * 继承 Thread 类的自定义线程类 */ public class MyThread extends Thread { private String threadName; // 构造方法,可以传入线程名称 public MyThread(String name) { this.threadName = name; } /** * 重写 run 方法,包含线程要执行的代码 * 注意:run() 方法是线程的入口点 */ @Override public void run() { System.out.println(threadName + " 线程开始执行..."); try { // 模拟耗时操作 for (int i = 1; i <= 5; i++) { System.out.println(threadName + " - 执行第 " + i + " 次任务"); // 休眠1秒,模拟任务执行时间 Thread.sleep(1000); // 可以调用其他方法 doSomething(i); } } catch (InterruptedException e) { System.out.println(threadName + " 被中断"); } System.out.println(threadName + " 线程执行完毕"); } /** * 线程中可以执行的其他方法 */ private void doSomething(int count) { System.out.println(threadName + " - 正在处理第" + count + "个数据"); } }使用 MyThread 创建线程并启动
/** * 主类,演示如何使用 MyThread 创建和启动线程 */ public class ThreadExample { public static void main(String[] args) { System.out.println("主线程开始执行..."); // 示例1: 创建单个线程 System.out.println("=== 示例1: 创建单个线程 ==="); // 第一步: 创建 MyThread 对象 MyThread thread1 = new MyThread("线程A"); // 第二步: 调用 start() 方法启动线程 thread1.start(); // 注意:不要调用 run() 方法 // 验证直接调用 run() 和调用 start() 的区别 System.out.println("\n=== 测试直接调用 run() 方法 ==="); MyThread testThread = new MyThread("测试线程"); // 直接调用 run() 方法(错误示例) System.out.println("直接调用 run() 方法:"); testThread.run(); // 这会在当前线程(主线程)中执行,不会创建新线程 System.out.println("\n=== 测试调用 start() 方法 ==="); MyThread testThread2 = new MyThread("正确线程"); testThread2.start(); // 正确:会在新线程中执行 // 主线程继续执行其他任务 for (int i = 0; i < 3; i++) { System.out.println("主线程执行任务: " + i); try { Thread.sleep(800); // 主线程休眠 } catch (InterruptedException e) { e.printStackTrace(); } } // 示例2: 创建多个线程 System.out.println("\n=== 示例2: 创建多个线程 ==="); MyThread thread2 = new MyThread("线程B"); MyThread thread3 = new MyThread("线程C"); // 启动多个线程 thread2.start(); thread3.start(); // 等待所有线程执行完毕 try { thread1.join(); // 等待线程1结束 thread2.join(); // 等待线程2结束 thread3.join(); // 等待线程3结束 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("所有线程执行完毕,主线程结束"); } }(2)实现接口(Runnable)
- 实现接口:public class MyRun implements Runnable{ }
- 重写run方法
- 实现线程中的代码
- 使用MyRun创建一个对象,将这个对象放在一个Thread类对象的构造方法中,使用Thread对象start方法
eg.创建实现 Runnable 接口的类
/** * 实现 Runnable 接口的类 */ public class MyRun implements Runnable { private String threadName; // 构造方法,可以传入线程名称 public MyRun(String name) { this.threadName = name; } /** * 重写 run 方法,包含线程要执行的代码 */ @Override public void run() { try { for (int i = 1; i <= 5; i++) { // 模拟耗时操作 Thread.sleep(1000); // 休眠1秒 // 打印当前线程信息 System.out.println(threadName + " - 正在执行: " + i); // 可以在run方法中调用其他方法 doSomething(i); } } catch (InterruptedException e) { System.out.println(threadName + " 被中断"); } System.out.println(threadName + " 执行完毕"); } /** * 线程中可以执行的其他方法 */ private void doSomething(int count) { System.out.println(threadName + " - 执行第" + count + "次操作"); } }使用 MyRun 创建线程并启动
/** * 主类,演示如何使用 MyRun 创建和启动线程 */ public class ThreadExample { public static void main(String[] args) { System.out.println("主线程开始执行..."); // 示例1: 创建单个线程 System.out.println("=== 示例1: 创建单个线程 ==="); // 第一步: 创建 MyRun 对象(实现了Runnable接口) MyRun myRun1 = new MyRun("线程A"); // 第二步: 将 MyRun 对象作为参数创建 Thread 对象 Thread thread1 = new Thread(myRun1); // 第三步: 调用 Thread 对象的 start() 方法启动线程 thread1.start(); // 主线程继续执行其他任务 for (int i = 0; i < 3; i++) { System.out.println("主线程执行任务: " + i); try { Thread.sleep(800); // 主线程休眠 } catch (InterruptedException e) { e.printStackTrace(); } } // 示例2: 创建多个线程 System.out.println("\n=== 示例2: 创建多个线程 ==="); MyRun myRun2 = new MyRun("线程B"); MyRun myRun3 = new MyRun("线程C"); Thread thread2 = new Thread(myRun2); Thread thread3 = new Thread(myRun3); // 启动多个线程 thread2.start(); thread3.start(); // 等待所有线程执行完毕 try { thread1.join(); // 等待线程1结束 thread2.join(); // 等待线程2结束 thread3.join(); // 等待线程3结束 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("所有线程执行完毕,主线程结束"); } }(3)实现Callable接口(最新,复杂)
目前所学知识无法实现,后续学到会补充
tips.同一个thread对象不可调用多次start函数(同一个线程在一个生命周期内(创建-运行-销毁)只能启动一次)
但同一个runnable接口类可以同时被多个thread运行
2.继承父类和实现接口两种创建线程的方式区别?
(1)继承机制
继承 Thread 类
// 继承 Thread 类 public class MyThread extends Thread { @Override public void run() { // 线程代码 } } // 使用 MyThread thread = new MyThread(); thread.start();实现 Runnable 接口
// 实现 Runnable 接口 public class MyRunnable implements Runnable { @Override public void run() { // 线程代码 } } // 使用 MyRunnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start();(2)核心区别对比表
| 对比点 | 继承 Thread 类 | 实现 Runnable 接口 |
|---|---|---|
| 继承限制 | 占用继承位置(Java单继承) | 不占用继承位置,可以继承其他类 |
| 代码结构 | 线程和任务耦合在一起 | 线程和任务分离,更清晰 |
| 资源共享 | 相对复杂 | 方便共享资源(同一个Runnable对象) |
| 扩展性 | 较差 | 更好,更灵活 |
| 面向对象 | 不符合"组合优于继承"原则 | 符合面向对象设计原则 |
(3)实际开发中的选择标准
选择继承 Thread 类的情况:
需要重写 Thread 类的其他方法(如 interrupt())
简单的单线程任务
不需要共享资源
学习或演示目的
选择实现 Runnable 接口的情况:
需要继承其他类(Java单继承限制)
需要多个线程共享资源
使用线程池管理线程
代码需要更好的可扩展性
大多数实际项目场景
在绝大多数情况下,应该优先使用实现 Runnable 接口的方式创建线程。只有在需要重写 Thread 类的特定方法时,才考虑继承 Thread 类。