目录
1.线程的概念
2.进程和线程的对比
3.线程创建的三种方式
1)继承Thread类
2)实现Runnable接口
3)实现Callable接口
三种方法的执行流程
1.线程的概念
线程是程序执行流的最小单位,是进程中的一个独立执行单元。一个进程可以包含多个线程,这些线程共享进程的资源,但各自独立执行。
2.进程和线程的对比
3.线程创建的三种方式
1)继承Thread类
/** * 第一种创建线程的方式,继承Thread类 * 优点:代码简单 * 缺点:单继承的局限性,不能再继承其他的类 */ public class Demo1 { public static void main(String[] args) { Thread myThread= new MyThread(); myThread.start(); for (int i = 0; i < 6; i++) { System.out.println("main方法输出:"+ i); } } } class MyThread extends Thread { public void run() { for (int i = 0; i < 6; i++) { System.out.println("子线程输出:" + i); } } }继承关系:MyThread extends Thread
重写方法:必须重写 run() 方法
启动方式:创建对象后调用 start() 方法
执行机制:
start() 会启动新线程,同时调用 run() 方法
直接调用 run() 只会普通方法调用,不会创建新线程
输出结果特点:主线程和子线程的输出会交替出现,顺序不确定
2)实现Runnable接口
/** * 第二种创建线程的方式:实现Runnable接口 * 优点:只是实现了接口,可以继承其他类,实现其他接口,拓展性强 * 缺点:需要多一个Runnable对象 */ public class Demo2 { // 目标:掌握多线程的第二种创建方式:实现runnable接口 public static void main(String[] args) { //3.创建线程对象,要把这个线程任务类包装成线程类,把这个对象交给线程去处理 Thread MyRunnable = new Thread(new MyRunnable()); MyRunnable.start(); for (int i = 0; i < 6; i++) { System.out.println("Main方法输出:" + i); } } } //1.定义一个线程任务类来实现Runnable接口 class MyRunnable implements Runnable { //2.重写run方法,执行线程任务 @Override public void run() { for (int i = 0; i < 6; i++) { System.out.println("子线程输出:" + i); } } }实现关系:MyRunnable implements Runnable
重写方法:必须实现 run() 方法
线程创建:需要将 Runnable 对象传给 Thread 构造器
优点:
避免单继承限制
适合多个线程执行相同任务
代码结构更清晰
简化写法
1.匿名内部类
/** * 线程创建的第二种方法的匿名内部类写法 */ public class Demo3 { public static void main(String[] args) { // 使用匿名内部类创建Runnable对象 Thread t1 = new Thread(new Runnable(){ @Override public void run() { for (int i = 0; i < 6; i++) { System.out.println("子线程输出:" + i); } } }); t1.start(); for (int i = 0; i < 6; i++) { System.out.println("Main方法输出:" + i); } } }2.lambda表达式
// Java 8+ 可以使用Lambda表达式进一步简化 public class Demo2_Lambda { public static void main(String[] args) { // 使用Lambda表达式创建Runnable对象 Thread thread = new Thread(() -> { for (int i = 0; i < 6; i++) { System.out.println("子线程输出:" + i); } }); thread.start(); for (int i = 0; i < 6; i++) { System.out.println("Main方法输出:" + i); } } }3)实现Callable接口
import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; /** * 前面两种创建方式重写的run方法都不能直接返回结果 * JDK5.0提供了Callable接口和FutureTask类来实现 * 第三种创建方式 */ public class Demo4 { public static void main(String[] args) { // 创建Callable接口的实现类对象 Callable<String> c = new MyCallable(100); // 4. 把Callable对象封装成真正的线程任务对象FutureTask对象 FutureTask<String> ft = new FutureTask<>(c); /** * FutureTask对象的作用 * a. 本质是一个Runnable线程任务对象,可以交给Thread线程对象处理 * b. 可以获取线程执行完毕后的结果 */ // 5. 把FutureTask对象作为参数传递给Thread对象 Thread thread = new Thread(ft); thread.start(); // 启动线程 // 创建第二个线程 Callable<String> c1 = new MyCallable(300); FutureTask<String> ft1 = new FutureTask<>(c1); Thread thread1 = new Thread(ft1); thread1.start(); try { // 获取第一个线程的执行结果 // get()方法会阻塞,直到线程执行完成并返回结果 System.out.println(ft.get()); } catch (Exception e) { e.printStackTrace(); } try { // 获取第二个线程的执行结果 System.out.println(ft1.get()); } catch (Exception e) { e.printStackTrace(); } } } // 1. 定义实现类来实现Callable接口 // 泛型参数表示返回值的类型 class MyCallable implements Callable<String> { private Integer n; // 构造方法,传入计算范围 MyCallable(Integer n) { this.n = n; } // 2. 重写call方法,有返回值,可以抛出异常 @Override public String call() throws Exception { Integer sum = 0; for (int i = 1; i <= n; i++) { sum += i; } return "1+.....+" + n + "的输出结果为" + sum; } }实现关系:MyCallable implements Callable<String>
重写方法:必须实现 call() 方法
关键区别:
call() 方法可以有返回值
call() 方法可以抛出异常
run() 方法没有返回值,不能抛出检查异常
执行流程:
Callable实现类 → FutureTask包装 → Thread包装 → start()启动 → get()获取结果
FutureTask 的作用:
适配器模式:将 Callable 转换成 Runnable
结果容器:存储异步计算结果
阻塞获取:get() 方法会等待线程执行完成
三种方法的执行流程
// 方式1:继承Thread Thread子类 → start() → JVM调用run() // 方式2:实现Runnable Runnable实现类 → 传给Thread → start() → JVM调用run() // 方式3:实现Callable Callable实现类 → FutureTask包装 → 传给Thread → start() → get()获取结果小结
优先选择实现 Runnable 接口,配合 Lambda 表达式
需要返回值时使用 Callable + FutureTask
避免直接继承 Thread 类
合理使用线程池管理线程资源