1. 基本概念
进程 vs 线程
- 进程:资源分配的最小单位,包含代码、数据和系统资源(如内存、文件句柄)。不同进程内存隔离。
- 线程:CPU调度的最小单位,隶属于进程,共享进程内存空间。一个进程可包含多个线程,实现并发执行。
2. 线程实现方式
继承 Thread 类
class MyThread extends Thread {
public void run() { /* 任务逻辑 */ }
}
MyThread t = new MyThread();
t.start();
实现 Runnable 接口
class MyRunnable implements Runnable {
public void run() { /* 任务逻辑 */ }
}
Thread t = new Thread(new MyRunnable());
t.start();
实现 Callable 接口(带返回值)
class MyCallable implements Callable<String> {
public String call() { return "结果"; }
}
FutureTask<String> future = new FutureTask<>(new MyCallable());
new Thread(future).start();
String result = future.get();
线程池(高效管理线程)
核心参数:
- 核心线程数:常驻线程数量。
- 最大线程数:临时线程上限(任务队列满时启用)。
-
任务队列:如
LinkedBlockingQueue(无界队列,易内存溢出)
ExecutorService pool = Executors.newFixedThreadPool(5); // 固定大小线程池
pool.submit(() -> { /* 任务逻辑 */ });
3. 线程生命周期
-
新建 (New):通过
new Thread()创建,未调用start()。 -
就绪 (Runnable):调用
start()后,等待CPU调度。 -
运行 (Running):获得CPU时间片,执行
run()方法。 -
阻塞 (Blocked):
-
等待阻塞:如
wait()、sleep()、join()。 -
同步阻塞:竞争锁失败(如
synchronized锁)。
-
等待阻塞:如
-
终止 (Terminated):
run()执行完毕或异常终止。
4. 同步与锁机制
synchronized 关键字
-
修饰代码块:
synchronized (锁对象) { /* 临界区代码 */ } // 锁对象需唯一 修饰方法:锁为当前对象实例(实例方法)或类对象(静态方法)。
显式锁 Lock 接口
Lock lock = new ReentrantLock(); // 可重入锁
lock.lock();
try { /* 临界区代码 */ } finally { lock.unlock(); }
读写锁 ReentrantReadWriteLock
ReadWriteLock rwLock = new ReentrantReadWriteLock();
rwLock.readLock().lock(); // 读锁(共享)
rwLock.writeLock().lock(); // 写锁(独占)
5.锁的分类
悲观锁:认为并发冲突概率高,操作前先加锁(如
synchronized、ReentrantLock)乐观锁:假设无并发冲突,通过版本号或 CAS(如
AtomicInteger)实现独享锁(排他锁):同一时刻仅允许一个线程访问资源(如
ReentrantLock、synchronized)共享锁:允许多个线程同时访问资源(如
ReadWriteLock的读锁,读共享,写独占)公平锁:按线程申请顺序获取锁(如
ReentrantLock(true))非公平锁:允许插队获取锁,吞吐量更高(如
synchronized、ReentrantLock默认模式)可重入锁:同一线程可重复获取同一锁(如
synchronized、ReentrantLock)不可重入锁:线程需释放锁后才能重新获取(如早期
Lock实现,现较少见)
6.CAS自旋锁
在 Java 中,CAS(Compare And Swap)锁是一种基于乐观锁的无锁并发机制,通过原子操作实现线程安全。
1.使用 Atomic 原子类
AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.compareAndSet(0, 1); // 如果当前值为0,则更新为1
2.自旋锁实现
public class SpinLock {
private AtomicBoolean locked = new AtomicBoolean(false);
public void lock() {
while (!locked.compareAndSet(false, true)) {
// 自旋等待,直到获取锁成功
}
}
public void unlock() {
locked.set(false);
}
}
| 类/接口 | 说明 | 示例方法 |
|---|---|---|
AtomicInteger |
对整型变量进行原子操作 |
getAndIncrement()、compareAndSet()
|
AtomicReference |
对引用类型变量进行原子操作 |
compareAndSet()、getAndUpdate()
|
AtomicStampedReference |
解决 ABA 问题(通过版本号控制) | compareAndSet(expected, new, stamp) |
注意事项与局限性
-
ABA 问题
若变量值从 A 变为 B 再变回 A,CAS 会误认为未变化。解决方法:- 使用
AtomicStampedReference记录版本号。 - 引入时间戳或递增计数器。
- 使用
-
自旋开销
CAS 在竞争激烈时会导致长时间自旋,浪费 CPU 资源。适用场景建议:- 并发冲突概率较低的场景25。
- 替代传统的
synchronized锁(如轻量级并发场景)。
-
仅支持单一变量
CAS 只能保证单个变量的原子性,多变量操作需结合其他同步机制(如锁)。
7.ReentrantLock
ReentrantLock 是 Java 并发编程中实现 Lock 接口的可重入锁,基于 AQS(AbstractQueuedSynchronizer) 框架实现,提供比 synchronized 更灵活的锁控制机制。
核心特性
-
可重入性
同一线程可多次获取同一锁,通过递增state变量实现(如递归调用场景)。 -
公平锁与非公平锁
- 非公平锁(默认):允许线程抢占锁,性能更高但可能导致饥饿。
- 公平锁:按线程请求顺序分配锁,遵循 FIFO 原则。
-
可中断与超时机制
-
lockInterruptibly()支持中断等待中的线程。 -
tryLock(long timeout, TimeUnit unit)允许在指定时间内尝试获取锁。
-
Lock lock = new ReentrantLock(); // 默认非公平锁
Lock fairLock = new ReentrantLock(true); // 公平锁
lock.lock();
try {
// 临界区操作
} finally {
lock.unlock(); // 必须显式释放锁
}
| 特性 | ReentrantLock |
synchronized |
|---|---|---|
| 锁获取方式 | 显式调用 lock()/unlock()
|
隐式通过代码块或方法 |
| 公平性 | 支持公平与非公平模式 | 仅非公平锁 |
| 中断响应 | 支持(lockInterruptibly()) |
不支持 |
| 条件队列 | 多个 Condition 对象 |
单一等待队列 |
| 性能 | 高并发场景下更优(非公平锁) | 较低版本 JDK 性能较差 |
实现原理
-
AQS 框架
通过内部类Sync继承AbstractQueuedSynchronizer,利用state变量记录锁状态和重入次数。- NonfairSync:非公平锁实现,直接尝试 CAS 抢占锁。
- FairSync:公平锁实现,先检查队列中是否有等待线程。
-
锁释放逻辑
每次unlock()调用递减state,当state=0时唤醒等待线程