Java 多线程编程详解
一、多线程基础概念
1、进程与线程
- 进程:操作系统资源分配的基本单位,每个进程中可包含多个线程。
- 线程:CPU 调度的基本单位,代表程序的一条执行路径。
2、并发与并行
- 并发:在单核 CPU 上通过任务切换实现多个任务交替执行。
- 并行:在多核 CPU 上真正同时执行多个任务。
3、线程生命周期
Java 中线程包括以下六种状态:
- New(新建)
- Runnable(可运行)
- Blocked(阻塞)
- Waiting(等待)
- Timed Waiting(超时等待)
- Terminated(终止)
4、死锁现象
死锁是指多个线程互相等待对方持有的资源,造成所有线程均无法继续执行的情况。
4.1 死锁形成条件
- 互斥条件:资源不能被多个线程同时使用。
- 请求和保持条件:线程已获得部分资源,并继续请求其他资源。
- 不可剥夺条件:线程已获得的资源不能被强制释放。
- 循环等待条件:存在线程间的资源循环等待链。
4.2 预防死锁策略
- 破坏请求和保持条件:一次性申请所有所需资源。
- 破坏不可剥夺条件:主动释放已占资源。
- 破坏循环等待条件:统一资源编号,按序申请。
二、多线程实现方式
1、创建线程的方法
1.1 继承 Thread 类
class WorkerThread extends Thread {
@Override
public void run() {
System.out.println("通过继承Thread类执行任务");
}
public static void main(String[] args) throws InterruptedException {
WorkerThread worker = new WorkerThread();
worker.start();
worker.join();
}
}
1.2 实现 Runnable 接口
class TaskRunner implements Runnable {
private final Object prevLock;
private final Object currLock;
private final String outputChar;
public TaskRunner(String ch, Object prev, Object curr) {
this.outputChar = ch;
this.prevLock = prev;
this.currLock = curr;
}
@Override
public void run() {
int counter = 10;
while (counter > 0) {
synchronized (prevLock) {
synchronized (currLock) {
System.out.print(outputChar);
counter--;
currLock.notifyAll();
}
try {
if (counter > 0) {
prevLock.wait();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Object lockA = new Object();
Object lockB = new Object();
Object lockC = new Object();
Thread th1 = new Thread(new TaskRunner("A", lockC, lockA));
Thread th2 = new Thread(new TaskRunner("B", lockA, lockB));
Thread th3 = new Thread(new TaskRunner("C", lockB, lockC));
th1.start();
Thread.sleep(50);
th2.start();
Thread.sleep(50);
th3.start();
th1.join();
th2.join();
th3.join();
}
}
1.3 实现 Callable 接口
import java.util.concurrent.*;
class AsyncTask implements Callable<String> {
@Override
public String call() {
return "异步任务执行完成";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
AsyncTask task = new AsyncTask();
FutureTask<String> future = new FutureTask<>(task);
new Thread(future).start();
System.out.println(future.get());
}
}
2、线程控制机制
2.1 等待与唤醒机制
- wait():使当前线程进入等待状态。
- notify():随机唤醒一个等待线程。
- notifyAll():唤醒所有等待线程。
2.2 条件等待机制
- await():类似 wait(),用于 Lock 框架。
- signal():类似 notify(),用于 Lock 框架。
- signalAll():类似 notifyAll(),用于 Lock 框架。
2.3 线程让步
- yield():暂停当前线程,让出 CPU 时间片。
3、线程常用 API
常见线程方法包括 sleep()、interrupt()、isAlive()、getState() 等。
三、线程池技术
1、线程池优势
- 减少线程创建销毁开销
- 提升响应速度
- 便于统一管理和监控
2、线程池创建
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
60L, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue<>(), // 工作队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
3、预定义线程池
3.1 固定大小线程池
ExecutorService fixedPool = Executors.newFixedThreadPool(4);
3.2 单线程池
ExecutorService singlePool = Executors.newSingleThreadExecutor();
3.3 缓存线程池
ExecutorService cachedPool = Executors.newCachedThreadPool();
3.4 定时任务线程池
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2);
scheduledPool.schedule(() -> System.out.println("延时任务"), 3, TimeUnit.SECONDS);
4、拒绝策略
- AbortPolicy:抛出异常拒绝新任务
- DiscardPolicy:静默丢弃新任务
- DiscardOldestPolicy:丢弃队列头部任务
- CallerRunsPolicy:由调用线程执行任务
四、线程安全保障
1、线程三大特性
- 原子性:操作不可分割
- 可见性:变量修改对其他线程立即可见
- 有序性:指令执行顺序不被重排序
2、同步机制
2.1 CAS 机制
CAS(Compare And Swap)是一种无锁算法,通过比较并交换实现原子操作。
2.2 Synchronized 关键字
public synchronized void method() {
// 同步方法
}
public void block() {
synchronized(this) {
// 同步代码块
}
}
2.3 ReentrantLock 锁
import java.util.concurrent.locks.ReentrantLock;
class Counter {
private int value = 0;
private final ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
value++;
} finally {
lock.unlock();
}
}
}
2.4 Volatile 关键字
Volatile 保证变量的可见性和禁止指令重排序,但不保证原子性。
3、线程局部变量
class LocalStorage {
private static ThreadLocal<String> storage = new ThreadLocal<>();
public static void main(String[] args) {
storage.set("主线程数据");
new Thread(() -> {
storage.set("子线程数据");
System.out.println(storage.get());
}).start();
System.out.println(storage.get());
}
}
