目录
1)继承Thread类继承Thread类)
2)实现Runnable接口实现Runnable接口)
3)实现Callable接口实现Callable接口)
1.线程的概念
线程是程序执行流的最小单位,是进程中的一个独立执行单元。一个进程可以包含多个线程,这些线程共享进程的资源,但各自独立执行。
2.进程和线程的对比

3.线程创建的三种方式
1)继承Thread类
java
/**
* 第一种创建线程的方式,继承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接口
java
/**
* 第二种创建线程的方式:实现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.匿名内部类
java
/**
* 线程创建的第二种方法的匿名内部类写法
*/
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
// 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接口
java
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() 方法会等待线程执行完成
三种方法的执行流程
java
// 方式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 类
-
合理使用线程池管理线程资源