Java并发编程:线程池、锁与原子类详解
一、线程池(ThreadPool)
1.1 为什么要使用线程池
- 资源复用:减少线程创建和销毁的开销
- 控制并发数:避免无限制创建线程导致系统崩溃
- 统一管理:提供线程生命周期管理
- 提高响应速度:任务到达时无需等待线程创建
1.2 核心接口和类
import java.util.concurrent.*;
// 1. Executor框架
ExecutorService executor = Executors.newFixedThreadPool(5);
// 2. ThreadPoolExecutor(最核心的实现)
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
60L, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new ArrayBlockingQueue<>(100), // 工作队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
1.3 四种线程池创建方式
// 1. 固定大小线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
// 2. 缓存线程池(可自动回收空闲线程)
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 3. 单线程线程池(保证任务顺序执行)
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
// 4. 调度线程池(支持定时/周期性任务)
ScheduledExecutorService scheduledThreadPool =
Executors.newScheduledThreadPool(3);
1.4 任务提交与执行
// 1. execute() - 执行无返回值任务
executor.execute(() -> {
System.out.println("执行任务");
});
// 2. submit() - 执行有返回值任务
Future<String> future = executor.submit(() -> {
Thread.sleep(1000);
return "任务结果";
});
// 获取结果
try {
String result = future.get(2, TimeUnit.SECONDS);
} catch (TimeoutException e) {
future.cancel(true);
}
// 3. invokeAll() - 批量执行任务
List<Callable<String>> tasks = new ArrayList<>();
// ... 添加任务
List<Future<String>> futures = executor.invokeAll(tasks);
1.5 线程池参数详解
/**
* 核心参数:
* 1. corePoolSize: 核心线程数,即使空闲也不会被回收
* 2. maximumPoolSize: 最大线程数
* 3. keepAliveTime: 非核心线程空闲存活时间
* 4. workQueue: 任务队列
* - ArrayBlockingQueue: 有界队列
* - LinkedBlockingQueue: 无界队列(默认Integer.MAX_VALUE)
* - SynchronousQueue: 不存储元素的队列
* - PriorityBlockingQueue: 优先级队列
* 5. threadFactory: 线程工厂
* 6. handler: 拒绝策略
* - AbortPolicy: 抛出RejectedExecutionException(默认)
* - CallerRunsPolicy: 由调用线程执行该任务
* - DiscardPolicy: 直接丢弃任务
* - DiscardOldestPolicy: 丢弃队列最前面的任务
*/
1.6 使用示例
public class ThreadPoolExample {
private static final int CORE_POOL_SIZE = 2;
private static final int MAX_POOL_SIZE = 4;
private static final long KEEP_ALIVE_TIME = 60L;
private static final int QUEUE_CAPACITY = 100;
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(QUEUE_CAPACITY),
new CustomThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy()
);
// 提交任务
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.execute(() -> {
System.out.println(Thread.currentThread().getName()
+ " 执行任务 " + taskId);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 优雅关闭
executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
}
static class CustomThreadFactory implements ThreadFactory {
private final AtomicInteger counter = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "CustomThread-" + counter.getAndIncrement());
thread.setDaemon(false);
thread.setPriority(Thread.NORM_PRIORITY);
return thread;
}
}
}
二、锁(Lock)
2.1 synchronized 关键字
public class SynchronizedExample {
// 1. 同步实例方法 - 锁住当前对象实例
public synchronized void instanceMethod() {
// 临界区
}
// 2. 同步静态方法 - 锁住当前类的Class对象
public static synchronized void staticMethod() {
// 临界区
}
// 3. 同步代码块 - 更细粒度的控制
public void syncBlock() {
// 锁住当前对象
synchronized(this) {
// 临界区
}
// 锁住类对象
synchronized(SynchronizedExample.class) {
// 临界区
}
// 锁住任意对象
private final Object lock = new Object();
synchronized(lock) {
// 临界区
}
}
}
2.2 Lock接口及其实现
import java.util.concurrent.locks.*;
public class LockExample {
// 1. ReentrantLock - 可重入锁
private final ReentrantLock lock = new ReentrantLock();
public void reentrantLockExample() {
lock.lock(); // 获取锁
try {
// 临界区代码
reentrantMethod(); // 可重入
} finally {
lock.unlock(); // 必须在finally中释放锁
}
}
private void reentrantMethod() {
lock.lock(); // 再次获取锁(重入)
try {
// ...
} finally {
lock.unlock();
}
}
// 2. 尝试获取锁
public void tryLockExample() {
if (lock.tryLock()) { // 尝试获取锁,立即返回
try {
// 获取锁成功
} finally {
lock.unlock();
}
} else {
// 获取锁失败,执行其他操作
}
}
// 3. 超时获取锁
public void tryLockWithTimeout() throws InterruptedException {
if (lock.tryLock(1, TimeUnit.SECONDS)) { // 等待1秒
try {
// 获取锁成功
} finally {
lock.unlock();
}
}
}
// 4. 可中断锁
public void interruptibleLock() throws InterruptedException {
lock.lockInterruptibly(); // 可被中断的获取锁
try {
// 临界区
} finally {
lock.unlock();
}
}
}
2.3 读写锁(ReadWriteLock)
public class ReadWriteLockExample {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
private Map<String, Object> cache = new HashMap<>();
// 读操作 - 共享锁
public Object get(String key) {
readLock.lock();
try {
return cache.get(key);
} finally {
readLock.unlock();
}
}
// 写操作 - 独占锁
public void put(String key, Object value) {
writeLock.lock();
try {
cache.put(key, value);
} finally {
writeLock.unlock();
}
}
// 锁降级示例
public void lockDegradation() {
writeLock.lock(); // 获取写锁
try {
// 执行写操作
cache.put("key", "value");
// 在释放写锁前获取读锁 - 锁降级
readLock.lock();
} finally {
writeLock.unlock(); // 释放写锁,但还持有读锁
}
try {
// 仍持有读锁,可以安全读取
Object value = cache.get("key");
} finally {
readLock.unlock();
}
}
}
2.4 StampedLock(JDK8+)
public class StampedLockExample {
private final StampedLock sl = new StampedLock();
private double x, y;
// 写锁
public void move(double deltaX, double deltaY) {
long stamp = sl.writeLock(); // 获取写锁
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp); // 释放写锁
}
}
// 乐观读
public double distanceFromOrigin() {
long stamp = sl.tryOptimisticRead(); // 尝试乐观读
double currentX = x, currentY = y;
if (!sl.validate(stamp)) { // 检查是否有写操作发生
stamp = sl.readLock(); // 升级为悲观读锁
try {
currentX = x;
currentY = y;
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
// 读锁
public double readValues() {
long stamp = sl.readLock();
try {
return x + y;
} finally {
sl.unlockRead(stamp);
}
}
}
2.5 条件变量(Condition)
public class ConditionExample {
private final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition(); // 条件:非空
private final Condition notFull = lock.newCondition(); // 条件:非满
private final Queue<String> queue = new LinkedList<>();
private final int CAPACITY = 10;
// 生产者
public void produce(String item) throws InterruptedException {
lock.lock();
try {
while (queue.size() == CAPACITY) {
notFull.await(); // 队列满,等待
}
queue.offer(item);
notEmpty.signal(); // 通知消费者
} finally {
lock.unlock();
}
}
// 消费者
public String consume() throws InterruptedException {
lock.lock();
try {
while (queue.isEmpty()) {
notEmpty.await(); // 队列空,等待
}
String item = queue.poll();
notFull.signal(); // 通知生产者
return item;
} finally {
lock.unlock();
}
}
}
三、原子类(Atomic Classes)
3.1 基本类型原子类
import java.util.concurrent.atomic.*;
public class AtomicBasicExample {
// 1. AtomicInteger
public void atomicIntegerDemo() {
AtomicInteger atomicInt = new AtomicInteger(0);
// 基本操作
atomicInt.set(10); // 设置值
int value = atomicInt.get(); // 获取值
atomicInt.lazySet(20); // 延迟设置
// 原子操作
int oldValue = atomicInt.getAndSet(30); // 获取并设置
int newValue = atomicInt.incrementAndGet(); // 自增并获取
int prevValue = atomicInt.getAndIncrement(); // 获取并自增
// CAS操作
boolean success = atomicInt.compareAndSet(30, 40); // 比较并交换
// 原子更新函数
atomicInt.updateAndGet(x -> x * 2); // JDK8+
atomicInt.accumulateAndGet(10, Integer::sum); // 累积计算
}
// 2. AtomicLong
public void atomicLongDemo() {
AtomicLong atomicLong = new AtomicLong(0L);
// 操作类似AtomicInteger
// 高性能计数器(避免伪共享)
LongAdder longAdder = new LongAdder();
longAdder.increment();
longAdder.add(10);
long sum = longAdder.sum();
}
// 3. AtomicBoolean
public void atomicBooleanDemo() {
AtomicBoolean atomicBool = new AtomicBoolean(false);
boolean oldValue = atomicBool.getAndSet(true);
boolean success = atomicBool.compareAndSet(true, false);
}
}
3.2 引用类型原子类
public class AtomicReferenceExample {
static class User {
private String name;
private int age;
// 构造方法、getter、setter省略
}
public void atomicReferenceDemo() {
// 1. AtomicReference
AtomicReference<User> atomicRef = new AtomicReference<>();
User user1 = new User("张三", 20);
User user2 = new User("李四", 25);
atomicRef.set(user1);
boolean success = atomicRef.compareAndSet(user1, user2);
// 2. 原子更新字段
AtomicIntegerFieldUpdater<User> ageUpdater =
AtomicIntegerFieldUpdater.newUpdater(User.class, "age");
User user = new User("王五", 30);
ageUpdater.incrementAndGet(user); // age变为31
// 3. AtomicStampedReference - 解决ABA问题
AtomicStampedReference<User> stampedRef =
new AtomicStampedReference<>(user1, 0);
int[] stampHolder = new int[1];
User currentUser = stampedRef.get(stampHolder);
int stamp = stampHolder[0];
// 带版本号的CAS
stampedRef.compareAndSet(currentUser, user2, stamp, stamp + 1);
// 4. AtomicMarkableReference - 标记引用
AtomicMarkableReference<User> markableRef =
new AtomicMarkableReference<>(user1, false);
boolean[] markHolder = new boolean[1];
User markedUser = markableRef.get(markHolder);
markableRef.compareAndSet(markedUser, user2, false, true);
}
}
3.3 数组原子类
public class AtomicArrayExample {
public void atomicArrayDemo() {
// 1. AtomicIntegerArray
int[] intArray = {1, 2, 3, 4, 5};
AtomicIntegerArray atomicIntArray = new AtomicIntegerArray(intArray);
// 原子更新指定位置的值
atomicIntArray.set(0, 10);
atomicIntArray.compareAndSet(1, 2, 20);
atomicIntArray.incrementAndGet(2);
// 2. AtomicLongArray
long[] longArray = {1L, 2L, 3L};
AtomicLongArray atomicLongArray = new AtomicLongArray(longArray);
// 3. AtomicReferenceArray
String[] strArray = {"A", "B", "C"};
AtomicReferenceArray<String> atomicRefArray =
new AtomicReferenceArray<>(strArray);
atomicRefArray.compareAndSet(0, "A", "D");
}
}
3.4 原子累加器(JDK8+)
public class AccumulatorExample {
public void accumulatorDemo() {
// 1. LongAdder - 比AtomicLong性能更好
LongAdder longAdder = new LongAdder();
// 多个线程并发累加
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executor.execute(() -> {
longAdder.increment();
});
}
executor.shutdown();
while (!executor.isTerminated()) {
// 等待所有任务完成
}
System.out.println("最终结果: " + longAdder.sum());
// 2. LongAccumulator - 更通用的累加器
LongAccumulator longAccumulator =
new LongAccumulator(Long::max, Long.MIN_VALUE);
longAccumulator.accumulate(10L);
longAccumulator.accumulate(20L);
longAccumulator.accumulate(15L);
System.out.println("最大值: " + longAccumulator.get());
// 3. DoubleAdder 和 DoubleAccumulator
DoubleAdder doubleAdder = new DoubleAdder();
doubleAdder.add(10.5);
doubleAdder.add(20.3);
DoubleAccumulator doubleAccumulator =
new DoubleAccumulator(Double::sum, 0.0);
}
}
四、综合应用示例
/**
* 线程安全的计数器示例
* 结合线程池、锁和原子类的使用
*/
public class ConcurrentCounter {
// 使用AtomicLong保证原子性
private final AtomicLong count = new AtomicLong(0);
// 使用ReadWriteLock保护复杂操作
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
// 使用线程池执行并发任务
private final ExecutorService executor =
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
/**
* 简单的原子递增
*/
public void increment() {
count.incrementAndGet();
}
/**
* 带条件判断的递增
*/
public boolean incrementIfLessThan(long max) {
while (true) {
long current = count.get();
if (current >= max) {
return false;
}
if (count.compareAndSet(current, current + 1)) {
return true;
}
// CAS失败,重试
}
}
/**
* 批量递增(使用锁)
*/
public void batchIncrement(int times) {
writeLock.lock();
try {
for (int i = 0; i < times; i++) {
count.incrementAndGet();
}
} finally {
writeLock.unlock();
}
}
/**
* 安全的获取值
*/
public long getCount() {
readLock.lock();
try {
return count.get();
} finally {
readLock.unlock();
}
}
/**
* 并发测试
*/
public void concurrentTest() throws InterruptedException {
int threadCount = 100;
int incrementsPerThread = 1000;
CountDownLatch latch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
executor.execute(() -> {
try {
for (int j = 0; j < incrementsPerThread; j++) {
increment();
}
} finally {
latch.countDown();
}
});
}
latch.await();
System.out.println("最终计数: " + getCount());
System.out.println("期望计数: " + (threadCount * incrementsPerThread));
executor.shutdown();
}
/**
* 性能比较:synchronized vs Lock vs Atomic
*/
public void performanceComparison() {
int iterations = 1000000;
// 1. synchronized方式
long start = System.currentTimeMillis();
Object lock = new Object();
long syncCount = 0;
for (int i = 0; i < iterations; i++) {
synchronized(lock) {
syncCount++;
}
}
long syncTime = System.currentTimeMillis() - start;
// 2. Atomic方式
start = System.currentTimeMillis();
AtomicLong atomicCount = new AtomicLong(0);
for (int i = 0; i < iterations; i++) {
atomicCount.incrementAndGet();
}
long atomicTime = System.currentTimeMillis() - start;
// 3. LongAdder方式
start = System.currentTimeMillis();
LongAdder adder = new LongAdder();
for (int i = 0; i < iterations; i++) {
adder.increment();
}
long adderTime = System.currentTimeMillis() - start;
System.out.println("synchronized耗时: " + syncTime + "ms");
System.out.println("AtomicLong耗时: " + atomicTime + "ms");
System.out.println("LongAdder耗时: " + adderTime + "ms");
}
}
五、最佳实践和注意事项
5.1 线程池最佳实践
-
合理配置线程池参数
- CPU密集型任务:线程数 = CPU核心数 + 1
- IO密集型任务:线程数 = CPU核心数 * 2
- 混合型任务:根据实际情况调整
-
避免使用无界队列
// 不推荐 new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); // 无界队列可能导致OOM // 推荐:使用有界队列并设置拒绝策略 new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1000), new ThreadPoolExecutor.CallerRunsPolicy()); -
优雅关闭线程池
executor.shutdown(); // 不再接受新任务 try { if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { executor.shutdownNow(); // 取消正在执行的任务 if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { System.err.println("线程池未正常关闭"); } } } catch (InterruptedException e) { executor.shutdownNow(); Thread.currentThread().interrupt(); }
5.2 锁的最佳实践
- 锁的范围尽量小
- 避免锁嵌套
- 使用tryLock避免死锁
- 释放锁写在finally块中
5.3 原子类注意事项
- ABA问题:使用
AtomicStampedReference解决 - 性能考虑:高并发场景使用
LongAdder代替AtomicLong - 复合操作:原子类只保证单个操作的原子性,复合操作需要额外同步
5.4 常见并发问题解决方案
| 问题 | 解决方案 |
|---|---|
| 死锁 | 1. 避免锁嵌套 2. 使用tryLock 3. 按固定顺序获取锁 |
| 活锁 | 1. 引入随机退避 2. 设置尝试次数限制 |
| 线程饥饿 | 1. 使用公平锁 2. 合理设置线程优先级 |
| 内存可见性 | 1. 使用volatile 2. 使用锁 3. 使用原子类 |
总结
Java并发编程的三个核心组件各有特点:
- 线程池:管理线程生命周期,提高资源利用率
- 锁:控制共享资源访问,保证线程安全
- 原子类:提供无锁线程安全操作,性能更高
在实际开发中,应根据具体场景选择合适的工具:
- 简单计数:使用原子类
- 复杂同步:使用锁
- 异步任务执行:使用线程池
- 读多写少:使用读写锁
- 高并发计数:使用LongAdder
掌握这些并发工具的使用和原理,能够帮助你编写出高效、线程安全的Java程序。