14、Java 并发编程进阶:JUC(java.util.concurrent)详解

【投稿赢 iPhone 17】「我的第一个开源项目」故事征集:用代码换C位出道! 10w+人浏览 1.6k人参与

开篇概览:JUC 的核心价值与学习路径

JUC(java.util.concurrent) 是 Java 并发编程的高级武器库,由并发大师 Doug Lea 设计,旨在解决传统 synchronizedwait/notify 机制的局限性:

  • 性能瓶颈:重量级锁开销大;
  • 功能单一:缺乏灵活的线程协调机制;
  • 易错性高:死锁、活锁、虚假唤醒等问题频发。

JUC 的核心思想是:通过显式锁、原子变量、高级同步器和线程池框架,提供更细粒度的控制、更高的吞吐量和更强大的功能

本章将严格遵循 “从基础到高级” 的学习路径,系统讲解 JUC 的四大阶段:

  1. 基石与核心概念(原子变量、显式锁);
  2. 线程管理与执行框架(线程池、Future);
  3. 高级同步器与并发数据结构(并发集合、同步器);
  4. 高级主题与性能优化(Fork/Join、CompletableFuture、底层原理)。

📌 学习前提

  • 已掌握基础多线程知识(ThreadRunnablesynchronized);
  • 理解 Java 内存模型(JMM)Happens-Before 原则

第一阶段:JUC 基石与核心概念

1. JUC 概述与 Happens-Before 原则

1.1 Happens-Before 原则(JMM 核心)

Happens-Before 定义了操作间的可见性与有序性规则,确保多线程程序的正确性。关键规则包括:

  • 程序顺序规则:单线程内,前操作 Happens-Before 后操作;
  • 监视器锁规则:解锁操作 Happens-Before 后续加锁操作;
  • volatile 变量规则:对 volatile 变量的写操作 Happens-Before 后续读操作;
  • 线程启动/终止规则start() Happens-Before 线程内任何操作;线程内操作 Happens-Before join() 返回。

意义:所有 JUC 组件(如 volatilesynchronizedLock)都基于此原则保证线程安全。


2. 原子变量类(Atomic Variables)

2.1 核心原理:CAS(Compare-And-Swap)
  • 硬件级原子指令CAS(value, expected, new)
  • 无锁编程:避免线程阻塞,提升并发性能;
  • ABA 问题:值从 A→B→A,CAS 误判为未修改(可用 AtomicStampedReference 解决)。
2.2 示例:AtomicInteger 与 ABA 问题
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

public class AtomicDemo {
    public static void main(String[] args) throws InterruptedException {
        // 1. AtomicInteger:无锁计数器
        AtomicInteger counter = new AtomicInteger(0);
        Thread[] threads = new Thread[10];
        
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    counter.incrementAndGet(); // 原子自增
                }
            });
            threads[i].start();
        }
        
        for (Thread t : threads) t.join();
        System.out.println("AtomicInteger 结果: " + counter.get()); // 10000

        // 2. AtomicStampedReference:解决 ABA 问题
        AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0);
        
        // 线程1:尝试将 A→B
        new Thread(() -> {
            int stamp = ref.getStamp();
            System.out.println("线程1 获取版本: " + stamp);
            try { Thread.sleep(100); } catch (InterruptedException e) {}
            boolean success = ref.compareAndSet("A", "B", stamp, stamp + 1);
            System.out.println("线程1 CAS 结果: " + success); // true
        }).start();
        
        // 线程2:模拟 ABA(A→C→A)
        new Thread(() -> {
            int stamp1 = ref.getStamp();
            ref.compareAndSet("A", "C", stamp1, stamp1 + 1);
            int stamp2 = ref.getStamp();
            ref.compareAndSet("C", "A", stamp2, stamp2 + 1);
            System.out.println("线程2 完成 ABA,当前版本: " + ref.getStamp());
        }).start();
        
        Thread.sleep(200);
    }
}

关键点

  • AtomicInteger 适用于简单计数场景
  • AtomicStampedReference 通过版本号解决 ABA 问题。

3. 显式锁(Explicit Locks)

3.1 ReentrantLock(可重入锁)
  • 优势:可中断、超时、公平锁;
  • 必须手动释放:在 finally 块中调用 unlock()
3.2 示例:ReentrantLock 高级特性
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;

public class ReentrantLockDemo {
    private final ReentrantLock lock = new ReentrantLock(true); // 公平锁
    private int count = 0;

    public void increment() {
        lock.lock(); // 获取锁
        try {
            count++;
        } finally {
            lock.unlock(); // 必须释放
        }
    }

    // 可中断的锁获取
    public void tryInterruptibly() throws InterruptedException {
        lock.lockInterruptibly(); // 可被 interrupt() 中断
        try {
            System.out.println("执行可中断任务");
        } finally {
            lock.unlock();
        }
    }

    // 尝试非阻塞获取锁
    public boolean tryLockWithTimeout() {
        try {
            if (lock.tryLock(1, TimeUnit.SECONDS)) { // 尝试等待1秒
                try {
                    System.out.println("成功获取锁");
                    return true;
                } finally {
                    lock.unlock();
                }
            } else {
                System.out.println("获取锁超时");
                return false;
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ReentrantLockDemo demo = new ReentrantLockDemo();
        
        // 测试可中断锁
        Thread t1 = new Thread(() -> {
            try {
                demo.tryInterruptibly();
            } catch (InterruptedException e) {
                System.out.println("线程被中断");
            }
        });
        t1.start();
        t1.interrupt(); // 中断线程
        
        // 测试超时锁
        demo.tryLockWithTimeout();
    }
}
3.3 ReadWriteLock(读写锁)
  • 读写分离:允许多个读线程并发,写线程独占;
  • 适用场景:读多写少(如缓存)。
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockDemo {
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private String data = "初始数据";

    // 读操作(共享锁)
    public String read() {
        lock.readLock().lock();
        try {
            return data;
        } finally {
            lock.readLock().unlock();
        }
    }

    // 写操作(独占锁)
    public void write(String newData) {
        lock.writeLock().lock();
        try {
            data = newData;
            System.out.println("数据已更新: " + newData);
        } finally {
            lock.writeLock().unlock();
        }
    }

    public static void main(String[] args) {
        ReadWriteLockDemo demo = new ReadWriteLockDemo();
        
        // 多个读线程并发执行
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                System.out.println("读取: " + demo.read());
            }).start();
        }
        
        // 写线程
        new Thread(() -> demo.write("新数据")).start();
    }
}

第二阶段:线程管理与执行框架

4. 线程池(ThreadPoolExecutor)

4.1 核心参数详解
参数说明
corePoolSize核心线程数(即使空闲也不回收)
maximumPoolSize最大线程数
keepAliveTime非核心线程空闲存活时间
workQueue任务队列(ArrayBlockingQueue, LinkedBlockingQueue
threadFactory线程工厂(自定义线程名)
handler拒绝策略(AbortPolicy, CallerRunsPolicy 等)
4.2 示例:自定义线程池
import java.util.concurrent.*;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 手动创建 ThreadPoolExecutor(避免 Executors 的陷阱)
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2,                    // corePoolSize
            4,                    // maximumPoolSize
            10,                   // keepAliveTime (秒)
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(2), // 有界队列
            new ThreadFactory() { // 自定义线程名
                private int count = 1;
                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r, "MyPool-Thread-" + count++);
                }
            },
            new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:由调用者线程执行
        );

        // 提交6个任务(2核心 + 2队列 + 2最大 = 6)
        for (int i = 1; i <= 6; i++) {
            final int taskId = i;
            executor.submit(() -> {
                System.out.println("任务 " + taskId + " 由 " + 
                    Thread.currentThread().getName() + " 执行");
                try { Thread.sleep(1000); } catch (InterruptedException e) {}
            });
        }

        // 关闭线程池
        executor.shutdown();
        try {
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
    }
}

拒绝策略

  • AbortPolicy:抛出 RejectedExecutionException(默认);
  • CallerRunsPolicy:由提交任务的线程执行;
  • DiscardPolicy:静默丢弃;
  • DiscardOldestPolicy:丢弃队列最旧任务。

5. Future 与 Callable

5.1 Callable vs Runnable
特性RunnableCallable
返回值有(V call()
异常不能抛出受检异常可抛出异常
5.2 示例:异步任务与结果获取
import java.util.concurrent.*;

public class FutureDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        
        // 提交 Callable 任务
        Callable<Integer> task = () -> {
            System.out.println("开始计算...");
            Thread.sleep(2000);
            return 42;
        };
        
        Future<Integer> future = executor.submit(task);
        
        // 异步检查任务状态
        while (!future.isDone()) {
            System.out.println("任务仍在执行...");
            Thread.sleep(500);
        }
        
        // 获取结果(阻塞)
        Integer result = future.get();
        System.out.println("计算结果: " + result); // 42
        
        executor.shutdown();
    }
}

第三阶段:高级同步器与并发数据结构

6. 并发集合

6.1 ConcurrentHashMap(高并发 HashMap)
  • JDK 8+:采用 CAS + synchronized 替代分段锁;
  • 高吞吐量:读操作无锁,写操作锁单个桶。
import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapDemo {
    public static void main(String[] args) throws InterruptedException {
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
        
        // 多线程安全写入
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            final int index = i;
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    map.compute("key" + index, (k, v) -> (v == null) ? 1 : v + 1);
                }
            });
            threads[i].start();
        }
        
        for (Thread t : threads) t.join();
        System.out.println("ConcurrentHashMap 大小: " + map.size()); // 10
    }
}
6.2 阻塞队列(BlockingQueue)
  • 生产者-消费者模式 的标准实现;
  • 自动阻塞:队列满时 put() 阻塞,空时 take() 阻塞。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class BlockingQueueDemo {
    public static void main(String[] args) {
        BlockingQueue<String> queue = new ArrayBlockingQueue<>(2);
        
        // 生产者
        new Thread(() -> {
            try {
                for (int i = 1; i <= 5; i++) {
                    queue.put("Item-" + i); // 队列满时阻塞
                    System.out.println("生产: Item-" + i);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }).start();
        
        // 消费者
        new Thread(() -> {
            try {
                while (true) {
                    String item = queue.take(); // 队列空时阻塞
                    System.out.println("消费: " + item);
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }).start();
    }
}

7. 同步器(Synchronizers)

7.1 CountDownLatch(倒计时门闩)
  • 一次性:计数归零后无法重置;
  • 典型场景:主线程等待多个子线程完成。
import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3);
        
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                System.out.println("服务启动中...");
                try { Thread.sleep(1000); } catch (InterruptedException e) {}
                latch.countDown(); // 计数减1
            }).start();
        }
        
        latch.await(); // 等待计数归零
        System.out.println("所有服务已启动,开始处理请求");
    }
}
7.2 CyclicBarrier(循环栅栏)
  • 可重用:到达屏障点后重置计数;
  • 典型场景:多线程分阶段计算。
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        // 栅栏动作:所有线程到达后执行
        CyclicBarrier barrier = new CyclicBarrier(3, () -> {
            System.out.println("=== 阶段完成,开始下一阶段 ===");
        });
        
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                try {
                    System.out.println("线程 " + Thread.currentThread().getName() + " 阶段1");
                    barrier.await(); // 等待其他线程
                    
                    System.out.println("线程 " + Thread.currentThread().getName() + " 阶段2");
                    barrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
7.3 Semaphore(信号量)
  • 控制并发数:类似“许可证”机制;
  • 典型场景:数据库连接池、限流。
import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
    // 模拟只有2个数据库连接
    private static final Semaphore semaphore = new Semaphore(2);
    
    public static void accessDatabase(String threadName) {
        try {
            semaphore.acquire(); // 获取许可证
            System.out.println(threadName + " 获取数据库连接");
            Thread.sleep(2000); // 模拟数据库操作
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            semaphore.release(); // 释放许可证
            System.out.println(threadName + " 释放数据库连接");
        }
    }
    
    public static void main(String[] args) {
        for (int i = 1; i <= 5; i++) {
            final String name = "线程-" + i;
            new Thread(() -> accessDatabase(name)).start();
        }
    }
}

第四阶段:高级主题与性能优化

8. Fork/Join 框架

8.1 核心思想:分而治之 + 工作窃取
  • Fork:任务拆分;
  • Join:结果合并;
  • 工作窃取:空闲线程从其他队列“窃取”任务。
8.2 示例:并行计算数组和
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;

public class ForkJoinDemo extends RecursiveTask<Long> {
    private final long[] array;
    private final int start;
    private final int end;
    private static final int THRESHOLD = 1000; // 拆分阈值

    public ForkJoinDemo(long[] array, int start, int end) {
        this.array = array;
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        if (end - start <= THRESHOLD) {
            // 小任务直接计算
            long sum = 0;
            for (int i = start; i < end; i++) {
                sum += array[i];
            }
            return sum;
        } else {
            // 大任务拆分
            int mid = (start + end) / 2;
            ForkJoinDemo leftTask = new ForkJoinDemo(array, start, mid);
            ForkJoinDemo rightTask = new ForkJoinDemo(array, mid, end);
            
            leftTask.fork(); // 异步执行左任务
            Long rightResult = rightTask.compute(); // 同步执行右任务
            Long leftResult = leftTask.join(); // 等待左任务结果
            
            return leftResult + rightResult;
        }
    }

    public static void main(String[] args) {
        long[] array = new long[10_000_000];
        for (int i = 0; i < array.length; i++) {
            array[i] = i;
        }
        
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinDemo task = new ForkJoinDemo(array, 0, array.length);
        long sum = pool.invoke(task);
        
        System.out.println("数组总和: " + sum);
        pool.shutdown();
    }
}

9. CompletableFuture(Java 8+)

9.1 核心优势:异步流水线
  • 非阻塞:避免 Future.get() 的阻塞;
  • 链式调用:组合多个异步任务;
  • 异常处理exceptionally() 统一处理。
9.2 示例:异步任务组合
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class CompletableFutureDemo {
    public static void main(String[] args) throws InterruptedException {
        // 1. 异步执行任务
        CompletableFuture<String> future1 = CompletableFuture
            .supplyAsync(() -> {
                sleep(1);
                return "Hello";
            });
        
        CompletableFuture<String> future2 = CompletableFuture
            .supplyAsync(() -> {
                sleep(2);
                return "World";
            });
        
        // 2. 组合两个任务的结果
        CompletableFuture<String> combined = future1
            .thenCombine(future2, (s1, s2) -> s1 + " " + s2)
            .thenApply(String::toUpperCase);
        
        // 3. 处理结果
        combined.thenAccept(System.out::println) // HELLO WORLD
                .join(); // 等待完成
        
        // 4. 异常处理
        CompletableFuture<String> errorFuture = CompletableFuture
            .supplyAsync(() -> {
                throw new RuntimeException("模拟异常");
            })
            .exceptionally(ex -> "默认值");
        
        System.out.println("异常处理结果: " + errorFuture.join()); // 默认值
    }
    
    private static void sleep(int seconds) {
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

10. 底层原理:AQS(AbstractQueuedSynchronizer)

10.1 AQS 核心设计
  • state:同步状态(如锁计数、信号量许可数);
  • CLH 队列:FIFO 等待队列(线程节点组成);
  • 模板方法:子类实现 tryAcquire()/tryRelease() 定义同步逻辑。
10.2 AQS 在 JUC 中的应用
组件AQS state 含义
ReentrantLock锁的持有次数
CountDownLatch计数器值
Semaphore剩余许可数
ReentrantReadWriteLock高16位读锁计数,低16位写锁计数

学习建议

  • ReentrantLock 源码入手,理解 NonfairSync/FairSync
  • 阅读 CountDownLatchSync 内部类,掌握 state 变化逻辑。

总结:JUC 学习全景图

阶段核心组件典型应用场景
基石Atomic*, ReentrantLock, ReadWriteLock无锁计数、灵活锁控制
线程管理ThreadPoolExecutor, Future任务调度、异步结果
高级同步ConcurrentHashMap, BlockingQueue, CountDownLatch高并发集合、生产者-消费者、线程协调
高级优化ForkJoinPool, CompletableFuture, AQS并行计算、异步流水线、底层原理

📌 终极建议
“先会用,再懂理;先模仿,再创新。”
AtomicIntegerThreadPoolExecutor 开始实践,逐步挑战 AQS 源码。掌握 JUC,你将具备设计高并发、高可用系统的硬核能力。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值