1-1-2 Java深度强化-java并发编程

38 阅读8分钟

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 线程池最佳实践

  1. 合理配置线程池参数

    • CPU密集型任务:线程数 = CPU核心数 + 1
    • IO密集型任务:线程数 = CPU核心数 * 2
    • 混合型任务:根据实际情况调整
  2. 避免使用无界队列

    // 不推荐
    new ThreadPoolExecutor(nThreads, nThreads, 
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<>());  // 无界队列可能导致OOM
    
    // 推荐:使用有界队列并设置拒绝策略
    new ThreadPoolExecutor(nThreads, nThreads,
        0L, TimeUnit.MILLISECONDS,
        new ArrayBlockingQueue<>(1000),
        new ThreadPoolExecutor.CallerRunsPolicy());
    
  3. 优雅关闭线程池

    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 锁的最佳实践

  1. 锁的范围尽量小
  2. 避免锁嵌套
  3. 使用tryLock避免死锁
  4. 释放锁写在finally块中

5.3 原子类注意事项

  1. ABA问题:使用AtomicStampedReference解决
  2. 性能考虑:高并发场景使用LongAdder代替AtomicLong
  3. 复合操作:原子类只保证单个操作的原子性,复合操作需要额外同步

5.4 常见并发问题解决方案

问题解决方案
死锁1. 避免锁嵌套 2. 使用tryLock 3. 按固定顺序获取锁
活锁1. 引入随机退避 2. 设置尝试次数限制
线程饥饿1. 使用公平锁 2. 合理设置线程优先级
内存可见性1. 使用volatile 2. 使用锁 3. 使用原子类

总结

Java并发编程的三个核心组件各有特点:

  • 线程池:管理线程生命周期,提高资源利用率
  • :控制共享资源访问,保证线程安全
  • 原子类:提供无锁线程安全操作,性能更高

在实际开发中,应根据具体场景选择合适的工具:

  • 简单计数:使用原子类
  • 复杂同步:使用锁
  • 异步任务执行:使用线程池
  • 读多写少:使用读写锁
  • 高并发计数:使用LongAdder

掌握这些并发工具的使用和原理,能够帮助你编写出高效、线程安全的Java程序。