引言:什么是线程?
线程(Thread)是程序执行的最小单位。一个进程(程序)可以包含多个线程,这些线程同时执行,互不干扰。
进程 vs 线程类比
想象一家餐厅:
├── 进程 = 整个餐厅
└── 线程 = 餐厅里的员工
一个餐厅(进程)可以有多个员工(线程)同时工作:
- 一个员工在炒菜
- 一个员工在洗碗
- 一个员工在招待客人
他们共享餐厅的资源,但同时做不同的事情
为什么需要多线程?
- 提高效率:多核CPU可以同时执行多个任务
- 防止阻塞:一个任务阻塞时,其他任务继续执行
- 提升性能:充分利用CPU空闲时间
线程的创建方式
方式1:继承Thread类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程执行中: " + Thread.currentThread().getName());
}
}
// 使用
MyThread thread = new MyThread();
thread.start(); // 启动线程(不是run()!)
方式2:实现Runnable接口(推荐)
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程执行中: " + Thread.currentThread().getName());
}
}
// 使用
Thread thread = new Thread(new MyRunnable());
thread.start();
方式3:使用Lambda表达式(Java 8+)
// 最简洁的方式
Thread thread = new Thread(() -> {
System.out.println("Lambda线程执行中!");
});
thread.start();
重要:启动线程必须调用start()方法,而不是run()方法。start()会创建新线程并执行run(),而run()只是普通方法调用,不会创建新线程。
线程的状态
线程在生命周期中会经历不同的状态:
| 状态 | 说明 |
|---|---|
| NEW | 新建状态,线程刚创建但未启动 |
| RUNNABLE | 可运行状态,正在Java虚拟机中执行或等待CPU |
| BLOCKED | 阻塞状态,等待获取锁 |
| WAITING | 等待状态,无限期等待另一个线程 |
| TIMED_WAITING | 计时等待状态,在指定时间后自动唤醒 |
| TERMINATED | 终止状态,线程执行完毕 |
获取线程状态
Thread thread = new Thread(() -> {
System.out.println("执行中...");
});
System.out.println(thread.getState()); // NEW
thread.start();
System.out.println(thread.getState()); // RUNNABLE
线程的常用方法
| 方法 | 说明 |
|---|---|
| start() | 启动线程 |
| run() | 线程执行的内容 |
| sleep(ms) | 让线程休眠指定毫秒 |
| join() | 等待线程执行完毕 |
| yield() | 让出CPU时间片 |
| interrupt() | 中断线程 |
| getName() | 获取线程名称 |
| getState() | 获取线程状态 |
| currentThread() | 获取当前线程对象 |
sleep() - 线程休眠
Thread thread = new Thread(() -> {
System.out.println("线程开始执行");
try {
Thread.sleep(2000); // 休眠2秒
System.out.println("线程2秒后继续执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
join() - 等待线程结束
Thread thread = new Thread(() -> {
System.out.println("子线程开始");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
System.out.println("子线程结束");
});
thread.start();
thread.join(); // 等待thread执行完毕
System.out.println("主线程继续执行");
线程同步
当多个线程访问共享资源时,需要进行同步控制,否则可能导致数据不一致。
问题演示:多线程抢票
class TicketSeller implements Runnable {
private int tickets = 10; // 共享资源
@Override
public void run() {
while (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "卖出第" + tickets + "张票");
tickets--;
try {
Thread.sleep(100); // 模拟网络延迟
} catch (InterruptedException e) {}
}
}
}
// 问题:多个线程可能卖出同一张票!
TicketSeller seller = new TicketSeller();
new Thread(seller, "窗口1").start();
new Thread(seller, "窗口2").start();
new Thread(seller, "窗口3").start();
synchronized - 同步方法
class TicketSeller implements Runnable {
private int tickets = 10;
@Override
public synchronized void run() { // 同步方法
while (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "卖出第" + tickets + "张票");
tickets--;
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
}
}
}
synchronized - 同步代码块
class TicketSeller implements Runnable {
private int tickets = 10;
private Object lock = new Object(); // 锁对象
@Override
public void run() {
while (true) {
synchronized (lock) { // 同步代码块
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "卖出第" + tickets + "张票");
tickets--;
} else {
break;
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
}
}
}
synchronized原理:每个对象都有一把锁(称为监视器锁)。当线程进入synchronized代码块时,必须获取该对象的锁,执行完毕后自动释放锁。
线程安全的类
Java提供了一些线程安全的类,可以在多线程环境下直接使用:
| 线程不安全的类 | 线程安全的替代 |
|---|---|
| ArrayList | Vector、CopyOnWriteArrayList |
| HashMap | ConcurrentHashMap |
| StringBuilder | StringBuffer |
StringBuffer线程安全示例
// StringBuffer是线程安全的,所有方法都用synchronized修饰
StringBuffer sb = new StringBuffer();
sb.append("Hello");
sb.append(" World");
System.out.println(sb.toString());
volatile关键字
volatile保证变量的可见性,即一个线程修改了volatile变量的值,其他线程能立即看到。
class VolatileDemo extends Thread {
private volatile boolean running = true; // volatile保证可见性
@Override
public void run() {
while (running) {
System.out.println("线程运行中...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {}
}
System.out.println("线程停止");
}
public void stopThread() {
running = false; // 主线程修改,子线程立即看到
}
}
小结
- 线程是程序执行的最小单位,提高程序效率
- 创建线程的方式:继承Thread或实现Runnable(推荐)
- 启动线程调用start(),不是run()
- synchronized实现线程同步,保证共享资源安全
- 线程状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED
- volatile保证变量可见性
☕ Java 在线代码编辑器
📝 运行结果