Java并发编程(JUC)

面向对象程序设计(Java)

掌握并发编程工具,提升程序性能和响应能力

前置知识要求

在学习 JUC 之前,你需要掌握以下基础知识:

📚 必备基础

  • 多线程基础:Thread、Runnable、synchronized等基本概念
  • 并发问题:理解原子性、可见性、有序性问题
  • 同步机制:wait/notify、volatile、ThreadLocal等工具
  • 死锁概念:了解死锁产生的原因和基本避免方法

✅ 基础检查清单

  • ☐ 能够正确创建和启动线程
  • ☐ 理解线程生命周期和状态转换
  • ☐ 掌握synchronized的使用场景
  • ☐ 能够识别和解决基本的竞态条件
  • ☐ 了解wait/notify模式的实现
📖 学习建议:如果以上基础不牢固,请先复习 15-multithread.html 的内容。JUC 建立在基础多线程概念之上,没有扎实的基础,直接学习JUC会感到困难。

基础多线程是学习JUC的前提,务必确保基础概念清晰后再开始JUC的学习旅程。

什么是 JUC?

java.util.concurrent 简称 JUC,是 Java 5 引入的并发工具包。它提供了一套强大的并发编程工具,大大简化了多线程开发。

🎯 为什么需要并发编程?

  • 提升性能:充分利用多核CPU的处理能力
  • 改善响应:避免耗时任务阻塞用户界面
  • 提高吞吐:同时处理多个请求或任务

📦 JUC 核心组件概览

// JUC 包的主要结构
java.util.concurrent           // 核心并发工具
├── Executor                    // 执行器框架
├── ExecutorService             // 线程池接口
├── ThreadPoolExecutor          // 线程池实现
├── Callable/Volatile           // 异步任务和原子操作
├── CountDownLatch/CyclicBarrier// 同步工具
├── Semaphore                   // 信号量
├── Lock                        // 显式锁
├── Concurrent collections      // 并发集合
└── Atomic classes              // 原子类

JUC 让并发编程从"手工作坊"时代进入了"工业化生产"时代,大大降低了并发编程的复杂度。

JUC 高级特性概览

在掌握多线程基础后,JUC 提供了更强大的高级特性来解决复杂的并发问题。

🚀 JUC 核心优势

相比传统多线程方式:

  • 性能卓越:CAS 无锁算法、分段锁、线程池优化
  • 功能丰富:异步编程、并行计算、复杂同步工具
  • 易用性高:丰富的API、完善的异常处理、内置监控
  • 可扩展:模块化设计,支持复杂业务场景

学习路径:

  1. 基础多线程 (已掌握) →
  2. JUC 线程池 →
  3. JUC 锁机制 →
  4. JUC 同步工具 →
  5. JUC 异步编程

📦 JUC 高级特性图谱

JUC 高级特性体系
├── 线程池体系 (ThreadPoolExecutor)
├── 锁机制 (ReentrantLock, ReadWriteLock)
├── 同步工具 (Semaphore, CountDownLatch, CyclicBarrier)
├── 定时任务 (ScheduledExecutorService)
├── 并行计算 (Fork/Join 框架)
├── 并发集合 (ConcurrentHashMap, BlockingQueue)
├── 原子类 (AtomicInteger, AtomicReference)
├── 无锁编程 (CAS 算法)
└── 异步编程 (CompletableFuture)

JUC 让并发编程从"能用"提升到"好用"和"高性能",是高级Java开发的必备技能。

线程池基础(Executor)

核心参数

参数 作用
corePoolSize核心线程数
maximumPoolSize最大线程数
keepAliveTime非核心线程存活时间
workQueue任务队列
threadFactory线程构造器
handler拒绝策略

ThreadPoolExecutor 示例

import java.util.concurrent.*;

public class PoolDemo {
    public static void main(String[] args) {
        ExecutorService pool = new ThreadPoolExecutor(
            2, 4, 60, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
        );

        for (int i = 0; i < 10; i++) {
            int id = i;
            pool.submit(() -> {
                System.out.println("任务" + id + " - " + Thread.currentThread().getName());
            });
        }

        pool.shutdown();
    }
}

合理设置核心数与队列容量,选择合适的拒绝策略,避免资源耗尽。

常用线程池工厂

ExecutorService fixed = Executors.newFixedThreadPool(4);
ExecutorService cached = Executors.newCachedThreadPool();
ExecutorService single = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(2);

scheduled.scheduleAtFixedRate(() -> {
    System.out.println("tick");
}, 0, 1, TimeUnit.SECONDS);
  • Fixed:固定并发,适合稳定负载
  • Cached:按需扩容,适合短任务但需警惕过度创建
  • Single:串行执行,适合顺序任务
  • Scheduled:定时/周期任务,替代 Timer

生产环境优先自定义 ThreadPoolExecutor,避免不受控的线程增长。

Callable与Future:带返回值的异步任务

前面我们学习了线程池提交 Runnable 任务,但如果任务需要返回结果呢?

🎯 Runnable vs Callable

  • Runnable:
    • 无返回值
    • 不能抛 checked 异常
    • void run()
  • Callable<V>:
    • 有返回值(泛型 V 指定返回类型)
    • 可以抛 checked 异常
    • V call() throws Exception

📦 Future 接口

  • V get():阻塞获取结果(会等待直到任务完成)
  • boolean cancel(boolean):尝试取消任务
  • boolean isDone():检查任务是否完成
  • boolean isCancelled():检查任务是否被取消
import java.util.concurrent.*;

public class CallableDemo {
    public static void main(String[] args) throws Exception {
        // 1. 定义 Callable 任务
        Callable<Integer> task = () -> {
            int sum = 0;
            for (int i = 1; i <= 100; i++) {
                sum += i;
            }
            return sum; // 返回计算结果
        };

        // 2. 创建线程池并提交任务
        ExecutorService pool = Executors.newFixedThreadPool(2);
        Future<Integer> future = pool.submit(task);

        // 3. 获取结果(会阻塞直到任务完成)
        Integer result = future.get();
        System.out.println("计算结果: " + result);

        // 4. 关闭线程池
        pool.shutdown();
    }
}

submit(Callable) 返回 Future 对象, 通过 future.get() 获取异步任务的结果。

💡 实际应用场景:并发计算

场景:计算 1 到 1000 的和,使用 10 个线程并行计算不同区间。

  • 线程 1 计算 1-100
  • 线程 2 计算 101-200
  • ...
  • 线程 10 计算 901-1000

最后汇总所有 Future 的结果。

// 并发计算示例
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<Integer>> futures = new ArrayList<>();

// 提交 10 个计算任务
for (int i = 0; i < 10; i++) {
    final int start = i * 100 + 1;
    Callable<Integer> task = () -> {
        int sum = 0;
        for (int j = start; j < start + 100; j++) {
            sum += j;
        }
        return sum;
    };
    futures.add(pool.submit(task));
}

// 汇总所有结果
int total = 0;
for (Future<Integer> f : futures) {
    total += f.get(); // 阻塞等待每个任务完成
}

System.out.println("总和: " + total); // 500500
pool.shutdown();
⚠️ 注意事项:
  • get() 阻塞:future.get() 会阻塞当前线程,直到任务完成
  • 超时获取:使用 future.get(5, TimeUnit.SECONDS) 避免无限等待
  • 异常处理:如果任务抛出异常,get() 会抛出 ExecutionException
  • 任务取消:调用 future.cancel(true) 可中断正在执行的任务

Callable + Future 是异步编程的基础。配合线程池可以实现高效的并发计算。 在 Java 8 中,还可以使用更强大的 CompletableFuture 进行链式调用和组合操作(详见本章后续内容)。

ThreadPoolExecutor 深度配置与监控 (1)

从基础线程池使用到生产级配置优化,JUC 提供了完整的线程池管理和监控能力。

🏗️ ThreadPoolExecutor 核心参数详解

基础参数(复习):

  • corePoolSize: 核心线程数(常驻)
  • maximumPoolSize: 最大线程数(扩容上限)
  • keepAliveTime: 非核心线程存活时间
  • workQueue: 任务队列类型和容量

高级参数:

  • threadFactory: 线程工厂(命名、优先级、守护状态)
  • handler: 拒绝策略(AbortPolicy、CallerRunsPolicy等)
  • allowCoreThreadTimeOut: 允许核心线程超时
  • prestartAllCoreThreads: 预启动核心线程

⚙️ 生产级线程池配置示例

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class ProductionThreadPoolConfig {

    // 自定义线程工厂
    static class CustomThreadFactory implements ThreadFactory {
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        CustomThreadFactory(String namePrefix) {
            this.namePrefix = namePrefix;
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r, namePrefix + "-" + threadNumber.getAndIncrement());
            t.setDaemon(false); // 非守护线程
            t.setPriority(Thread.NORM_PRIORITY);
            // 设置未捕获异常处理器
            t.setUncaughtExceptionHandler((thread, ex) -> {
                System.err.printf("Thread %s threw exception: %s%n",
                    thread.getName(), ex.getMessage());
            });
            return t;
        }
    }

    // CPU密集型线程池配置
    public static ThreadPoolExecutor cpuIntensivePool() {
        int coreCount = Runtime.getRuntime().availableProcessors();
        return new ThreadPoolExecutor(
            coreCount,           // 核心线程数 = CPU核数
            coreCount,           // 最大线程数 = CPU核数(无扩容)
            0L, TimeUnit.MILLISECONDS,  // 核心线程无超时
            new LinkedBlockingQueue<>(1000), // 有界队列
            new CustomThreadFactory("CPU-Pool"),
            new ThreadPoolExecutor.AbortPolicy() // 队列满则拒绝
        );
    }

    // IO密集型线程池配置
    public static ThreadPoolExecutor ioIntensivePool() {
        int coreCount = Runtime.getRuntime().availableProcessors();
        return new ThreadPoolExecutor(
            coreCount * 2,       // 核心线程数 = CPU核数 * 2
            coreCount * 4,       // 最大线程数 = CPU核数 * 4
            60L, TimeUnit.SECONDS, // 非核心线程60秒超时
            new LinkedBlockingQueue<>(2000), // 更大的队列
            new CustomThreadFactory("IO-Pool"),
            new ThreadPoolExecutor.CallerRunsPolicy() // 队列满时调用者执行
        );
    }
}

合理的线程池配置需要根据任务类型(CPU密集型/IO密集型)和系统资源进行调优。

线程池监控与调优 (2)

生产环境中需要对线程池进行实时监控和动态调优,确保系统稳定性和性能。

📊 线程池核心监控指标

基础监控指标:

  • getActiveCount(): 正在执行任务的线程数
  • getPoolSize(): 当前线程池总线程数
  • getCorePoolSize(): 核心线程数
  • getMaximumPoolSize(): 最大线程数
  • getQueue().size(): 队列中等待任务数

性能监控指标:

  • getCompletedTaskCount(): 已完成任务数
  • getTaskCount(): 总提交任务数
  • isShutdown(): 是否已关闭
  • isTerminated(): 是否已终止
  • isTerminating(): 是否正在终止

🚀 线程池监控系统实现

public class ThreadPoolMonitor {
    private final ThreadPoolExecutor executor;
    private final ScheduledExecutorService monitor;

    public ThreadPoolMonitor(ThreadPoolExecutor executor) {
        this.executor = executor;
        this.monitor = Executors.newSingleThreadScheduledExecutor();
        startMonitoring();
    }

    private void startMonitoring() {
        monitor.scheduleAtFixedRate(() -> {
            try {
                // 收集监控数据
                int activeCount = executor.getActiveCount();
                int poolSize = executor.getPoolSize();
                int corePoolSize = executor.getCorePoolSize();
                int maximumPoolSize = executor.getMaximumPoolSize();
                long completedTaskCount = executor.getCompletedTaskCount();
                long taskCount = executor.getTaskCount();
                int queueSize = executor.getQueue().size();

                // 计算性能指标
                double completionRate = taskCount > 0 ?
                    (double) completedTaskCount / taskCount : 0.0;
                double queueUtilization = maximumPoolSize > 0 ?
                    (double) queueSize / (queueSize + poolSize) : 0.0;

                // 输出监控信息
                System.out.printf("=== 线程池监控 ===%n");
                System.out.printf("活跃线程: %d/%d (核心: %d, 最大: %d)%n",
                    activeCount, poolSize, corePoolSize, maximumPoolSize);
                System.out.printf("任务完成: %d/%d (%.2f%%)%n",
                    completedTaskCount, taskCount, completionRate * 100);
                System.out.printf("队列大小: %d (利用率: %.2f%%)%n",
                    queueSize, queueUtilization * 100);

                // 告警检查
                checkAlerts(activeCount, poolSize, queueSize, completionRate);

            } catch (Exception e) {
                System.err.println("监控异常: " + e.getMessage());
            }
        }, 0, 5, TimeUnit.SECONDS);
    }
}

实时监控和动态调优是生产环境线程池管理的关键技能。

Lock 与可重入锁

  • ReentrantLock 提供可重入、可中断、可限时的锁获取
  • 支持公平锁(按等待顺序)与非公平锁(默认)
  • 必须配合 try/finally 保证 unlock
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class LockDemo {
    private final ReentrantLock lock = new ReentrantLock(true);
    public void work() throws InterruptedException {
        if (lock.tryLock(100, TimeUnit.MILLISECONDS)) {
            try {
                // 临界区
            } finally {
                lock.unlock();
            }
        } else {
            System.out.println("获取锁超时");
        }
    }
}

tryLock 超时可降低等待引发的死锁风险,提升系统弹性。

Condition 与生产者/消费者

import java.util.LinkedList;
import java.util.concurrent.locks.*;

class PCQueue<T> {
    private final LinkedList<T> q = new LinkedList<>();
    private final int cap;
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notEmpty = lock.newCondition();
    private final Condition notFull = lock.newCondition();

    PCQueue(int cap) { this.cap = cap; }

    public void put(T x) throws InterruptedException {
        lock.lock();
        try {
            while (q.size() == cap) notFull.await();
            q.add(x);
            notEmpty.signal();
        } finally { lock.unlock(); }
    }

    public T take() throws InterruptedException {
        lock.lock();
        try {
            while (q.isEmpty()) notEmpty.await();
            T x = q.removeFirst();
            notFull.signal();
            return x;
        } finally { lock.unlock(); }
    }
}

Condition 比 wait/notify 语义更清晰,支持多条件队列,避免虚假唤醒。

BlockingQueue 版本

import java.util.concurrent.*;

BlockingQueue<Integer> q = new ArrayBlockingQueue<>(10);

Runnable producer = () -> {
    try {
        for (int i = 0; i < 100; i++) q.put(i);
    } catch (InterruptedException ignored) {}
};

Runnable consumer = () -> {
    try {
        while (true) {
            Integer x = q.take();
            // 处理 x
        }
    } catch (InterruptedException ignored) {}
};

优先使用阻塞队列,减少并发细节错误,提升鲁棒性。

高级锁机制与死锁排查

掌握 JUC 锁机制的高级特性,学会识别和解决死锁问题。

🔒 ReentrantReadWriteLock:读写分离锁

读写锁特性:

  • 读锁共享:多个线程可以同时持有读锁
  • 写锁独占:只有一个线程可以持有写锁
  • 读写互斥:读锁和写锁不能同时存在
  • 锁降级:写锁可以降级为读锁

适用场景:

  • 读多写少的数据结构
  • 缓存系统
  • 配置管理器
  • 统计计数器

🚨 死锁识别与排查

public class DeadlockDetection {

    // 死锁的四个必要条件
    public static void analyzeDeadlockConditions() {
        System.out.println("死锁的四个必要条件:");
        System.out.println("1. 互斥条件:资源不能被多个线程同时使用");
        System.out.println("2. 占有并等待:线程持有资源的同时等待其他资源");
        System.out.println("3. 不可抢占:资源不能被强制性地抢占");
        System.out.println("4. 循环等待:存在线程资源的环形等待链");
    }

    // 死锁预防策略
    public static class DeadlockPrevention {

        // 1. 锁排序:所有线程按固定顺序获取锁
        private static final Object lock1 = new Object();
        private static final Object lock2 = new Object();

        public static void lockOrderingExample() {
            // 所有线程都按 lock1 -> lock2 的顺序获取锁
            synchronized (lock1) {
                System.out.println("获取 lock1");
                synchronized (lock2) {
                    System.out.println("获取 lock2 - 不会死锁");
                }
            }
        }

        // 2. 超时机制:使用 tryLock 避免无限等待
        public static void timeoutExample() throws InterruptedException {
            ReentrantLock lock1 = new ReentrantLock();
            ReentrantLock lock2 = new ReentrantLock();

            // 先尝试获取 lock1,再尝试获取 lock2
            if (lock1.tryLock(1, TimeUnit.SECONDS)) {
                try {
                    if (lock2.tryLock(1, TimeUnit.SECONDS)) {
                        try {
                            System.out.println("成功获取两个锁");
                        } finally {
                            lock2.unlock();
                        }
                    } else {
                        System.out.println("获取 lock2 超时,释放 lock1");
                    }
                } finally {
                    lock1.unlock();
                }
            }
        }
    }
}

掌握死锁预防和检测技巧是编写可靠并发程序的关键。

Semaphore 信号量

  • 控制同时访问的许可数,常用于限流与连接池
  • 支持公平模式,保证等待顺序
import java.util.concurrent.Semaphore;

public class SemDemo {
    static final Semaphore sem = new Semaphore(3, true);
    static void serve() {
        try {
            sem.acquire();
            // 处理请求
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            sem.release();
        }
    }
}

以许可替代锁的互斥,适合资源槽位管理与并发限速。

同步工具类:协调线程执行

JUC 提供了多种同步工具类,用于协调多个线程的执行顺序。

🚦 CountDownLatch:倒计时门闩

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个 CountDownLatch,计数为 3
        CountDownLatch latch = new CountDownLatch(3);

        // 启动 3 个工作线程
        for (int i = 0; i < 3; i++) {
            int workerId = i;
            new Thread(() -> {
                try {
                    System.out.println("工作线程 " + workerId + " 开始工作");
                    Thread.sleep(2000); // 模拟工作
                    System.out.println("工作线程 " + workerId + " 完成工作");
                    latch.countDown(); // 计数减 1
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }).start();
        }

        // 主线程等待所有工作线程完成
        System.out.println("主线程等待所有工作线程完成...");
        latch.await(); // 阻塞,直到计数为 0
        System.out.println("所有工作线程都已完成,主线程继续执行");
    }
}

🔄 CyclicBarrier:循环栅栏

public class CyclicBarrierExample {
    public static void main(String[] args) {
        // 创建 CyclicBarrier,等待 3 个线程,到达后执行指定动作
        CyclicBarrier barrier = new CyclicBarrier(3, () -> {
            System.out.println("=== 所有线程都到达屏障点,执行汇总操作 ===");
        });

        for (int i = 0; i < 3; i++) {
            int threadId = i;
            new Thread(() -> {
                try {
                    System.out.println("线程 " + threadId + " 第一个阶段");
                    Thread.sleep(1000);

                    System.out.println("线程 " + threadId + " 到达屏障点,等待其他线程");
                    barrier.await(); // 等待其他线程

                    System.out.println("线程 " + threadId + " 第二个阶段");

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

定时任务:Timer vs ScheduledExecutor

import java.util.Timer;
import java.util.TimerTask;

Timer timer = new Timer();
timer.schedule(new TimerTask() {
    public void run() { System.out.println("once"); }
}, 1000);

Timer 为单线程,任务异常会终止,延迟易叠加。

import java.util.concurrent.*;

ScheduledExecutorService ses = Executors.newScheduledThreadPool(2);
ses.scheduleAtFixedRate(() -> {
    System.out.println("fixed-rate");
}, 0, 1, TimeUnit.SECONDS);

ses.scheduleWithFixedDelay(() -> {
    System.out.println("fixed-delay");
}, 0, 1, TimeUnit.SECONDS);

建议使用 ScheduledExecutorService,支持多线程与健壮的调度语义。

Fork/Join 框架

  • 工作窃取算法提升吞吐,适合递归分治
  • 核心类型:ForkJoinPool、RecursiveTask/Action
import java.util.concurrent.*;

class SumTask extends RecursiveTask<Long> {
    private final long[] arr; private final int start, end; private static final int THRESHOLD = 10_000;
    SumTask(long[] arr, int start, int end) { this.arr = arr; this.start = start; this.end = end; }
    protected Long compute() {
        int len = end - start;
        if (len <= THRESHOLD) {
            long sum = 0; for (int i = start; i < end; i++) sum += arr[i]; return sum;
        }
        int mid = start + len / 2;
        SumTask left = new SumTask(arr, start, mid);
        SumTask right = new SumTask(arr, mid, end);
        left.fork();
        long r = right.compute();
        long l = left.join();
        return l + r;
    }
}

ForkJoinPool pool = ForkJoinPool.commonPool();
long[] data = new long[5_000_000];
long result = pool.invoke(new SumTask(data, 0, data.length));

选择合理的阈值,避免过度拆分造成调度开销大于计算收益。

Fork/Join 套路

六步走

  • 场景判断:CPU 密集、可分治、结果可合并
  • 任务建模:定义 RecursiveTask/Action 表达"拆分+合并"
  • 阈值设计:小任务走顺序算法,大任务继续拆分
  • 拆分策略:二分或 K 分,尽量均衡工作量
  • 合并策略:先右 compute、左 join,或双 fork 后 join
  • 提交执行:ForkJoinPool.invoke/commonPool 并行运行

口诀:先判断再拆分,右算左合并,阈值要合理,提交后验证。

模板代码

import java.util.concurrent.*;

class DivideTask extends RecursiveTask<Long> {
    private final long[] arr;
    private final int start, end;
    private static final int THRESHOLD = 10_000;

    DivideTask(long[] arr, int start, int end) {
        this.arr = arr; this.start = start; this.end = end;
    }

    protected Long compute() {
        int len = end - start;
        if (len <= THRESHOLD) {
            long sum = 0;
            for (int i = start; i < end; i++) sum += arr[i];
            return sum;
        }
        int mid = start + len / 2;
        DivideTask left = new DivideTask(arr, start, mid);
        DivideTask right = new DivideTask(arr, mid, end);
        left.fork();
        long r = right.compute();
        long l = left.join();
        return l + r;
    }
}

public class UseForkJoin {
    public static void main(String[] args) {
        long[] data = new long[5_000_000];
        ForkJoinPool pool = ForkJoinPool.commonPool();
        long result = pool.invoke(new DivideTask(data, 0, data.length));
        System.out.println(result);
    }
}

将"顺序算法"封装为小任务,用二分递归拆分并在池中并行执行。

同步集合 vs 并发集合

同步集合 (Synchronized Collections)

import java.util.*;

List<String> syncList = Collections.synchronizedList(new ArrayList<>());
syncList.add("a");

// 迭代需要外部同步
synchronized (syncList) {
    for (String s : syncList) { /* 处理 */ }
}

Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());
  • 只提供整体互斥,不保证迭代过程的原子性
  • Fail-Fast 迭代器在结构修改时抛 ConcurrentModificationException

并发集合 (Concurrent Collections)

// ConcurrentHashMap:线程安全的 HashMap
ConcurrentHashMap concurrentMap = new ConcurrentHashMap<>();
concurrentMap.putIfAbsent("key", 100);

// CopyOnWriteArrayList:写时复制的 List,适合读多写少
CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();

// 阻塞队列
ArrayBlockingQueue arrayQueue = new ArrayBlockingQueue<>(100);
LinkedBlockingQueue linkedQueue = new LinkedBlockingQueue<>();
PriorityBlockingQueue priorityQueue = new PriorityBlockingQueue<>();
  • 使用分段锁、CAS等高性能并发算法
  • 迭代器弱一致性,不会抛出并发修改异常

在高并发场景优先选择并发集合,降低锁竞争与迭代风险。

无锁编程与原子类深入

CAS(Compare-And-Swap)是无锁编程的核心,Java 原子类基于 CAS 实现高性能的线程安全操作。

🔄 CAS 原理详解

CAS 操作三要素:

  • V: 内存中的变量值
  • E: 期望的旧值
  • N: 要更新的新值

CAS(V, E, N): 如果 V == E,则 V = N,否则不做任何操作

CAS 优势:

  • 无阻塞:无需获取锁,避免线程阻塞
  • 高性能:无锁竞争,减少上下文切换
  • 死锁免疫:没有锁,自然无死锁问题

⚡ 原子类实战应用

public class AtomicExample {
    // 基本类型原子类
    private AtomicInteger atomicInt = new AtomicInteger(0);
    private AtomicLong atomicLong = new AtomicLong(0L);
    private AtomicBoolean atomicBool = new AtomicBoolean(false);

    // 引用类型原子类
    private AtomicReference atomicRef = new AtomicReference<>();

    public void demonstrateAtomicOperations() {
        // 原子递增
        int result1 = atomicInt.incrementAndGet(); // 先加1再返回
        int result2 = atomicInt.getAndIncrement(); // 先返回再加1

        // 原子比较并设置(CAS)
        boolean success = atomicInt.compareAndSet(5, 100); // 如果当前值是5,则设置为100

        // 原子操作链
        atomicInt.updateAndGet(x -> x * 2); // 原子地执行函数操作
        atomicInt.accumulateAndGet(10, (x, y) -> x + y); // 原子地累加
    }

    // CAS 操作示例:只有当前值为期望值时才更新
    public boolean incrementIfEven() {
        int current, next;
        do {
            current = atomicInt.get();
            if (current % 2 != 0) {
                return false; // 如果是奇数,不执行更新
            }
            next = current + 1;
        } while (!atomicInt.compareAndSet(current, next));

        return true;
    }
}

原子类提供了高性能的线程安全操作,特别适合计数器、状态标志等场景。

CompletableFuture 深度编程

CompletableFuture 是 Java 8 引入的革命性异步编程工具,支持链式调用和组合操作。

🎯 为什么需要 CompletableFuture?

传统 Future 的局限:

  • 只能通过 get() 阻塞获取结果
  • 无法组合多个异步操作
  • 异常处理机制复杂
  • 不支持回调机制

CompletableFuture 的优势:

  • 非阻塞:thenAccept, thenApply 等回调
  • 可组合:thenCompose, thenCombine 链式操作
  • 异常友好:exceptionally 统一异常处理
  • 批量操作:allOf, anyOf 多任务协调

⚡ 链式调用示例

public class CompletableFutureChaining {
    public static void main(String[] args) throws Exception {
        CompletableFuture future = CompletableFuture
            .supplyAsync(() -> "用户数据")

            // 转换操作
            .thenApply(data -> {
                System.out.println("1. 处理: " + data);
                return data + " -> 增加权限";
            })

            // 消费操作
            .thenAccept(processedData -> {
                System.out.println("2. 消费: " + processedData);
            })

            // 后续操作
            .thenRun(() -> {
                System.out.println("3. 清理资源");
            });

        // 等待完成
        future.get();
    }
}

链式调用让异步编程变得直观,可以清晰地表达业务流程。

JUC 最佳实践和常见陷阱

✅ 最佳实践

  • 优先使用线程池:避免手动创建和管理线程
  • 合理设置线程池参数:根据任务类型(CPU密集型/IO密集型)调整
  • 使用合适的并发集合:根据场景选择 ConcurrentHashMap、CopyOnWriteArrayList 等
  • 避免锁竞争:使用分段锁、读写锁或无锁数据结构
  • 正确处理异常:在异步任务中妥善处理异常
  • 资源清理:确保 ExecutorService 等资源被正确关闭

❌ 常见陷阱

// 陷阱1:线程池未关闭导致资源泄漏
public class BadPractice1 {
    public void badMethod() {
        ExecutorService executor = Executors.newFixedThreadPool(10);
        executor.submit(() -> System.out.println("任务执行"));
        // 忘记调用 executor.shutdown()
    }
}

// 陷阱2:错误地使用 synchronized 替代 JUC 工具
public class BadPractice2 {
    private final List list = new ArrayList<>();

    public void addItem(String item) {
        synchronized (list) { // 不如使用 CopyOnWriteArrayList
            list.add(item);
        }
    }
}

// 陷阱3:阻塞操作忘记处理 InterruptedException
public class BadPractice3 {
    public void badSleep() {
        try {
            Thread.sleep(1000); // 可能被中断但未处理
        } catch (InterruptedException e) {
            // 错误:忽略中断信号
        }
    }
}

🔍 性能调优建议

// 性能调优示例
public class PerformanceOptimization {

    // CPU 密集型任务:线程数 = CPU 核心数
    public ExecutorService cpuIntensivePool() {
        int cores = Runtime.getRuntime().availableProcessors();
        return new ThreadPoolExecutor(
            cores, cores, 0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>()
        );
    }

    // IO 密集型任务:线程数可以适当增加
    public ExecutorService ioIntensivePool() {
        int cores = Runtime.getRuntime().availableProcessors();
        return new ThreadPoolExecutor(
            cores * 2, cores * 4, 60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(1000)
        );
    }
}

掌握 JUC 不仅能提升程序性能,还能写出更健壮、可维护的并发代码。实践是最好的老师,多写多练才能精通。

1 / X