知识点:深入剖析 Java 并发工具类

一、并发工具类概述

在 Java 多线程编程中,java.util.concurrent 包提供了一系列强大的并发工具类,这些工具类极大地简化了复杂并发场景的开发。它们主要分为以下几类:

  1. 同步辅助类 :如 CountDownLatchCyclicBarrierSemaphore
  2. 并发集合 :如 ConcurrentHashMapCopyOnWriteArrayList
  3. 原子类 :如 AtomicIntegerAtomicReference
  4. 线程池相关 :如 ThreadPoolExecutorExecutors

本文将重点探讨同步辅助类的核心实现原理和高级应用技巧。

二、CountDownLatch:线程计数器

核心原理

CountDownLatch 通过一个计数器实现线程间的同步。当一个或多个线程调用 await() 方法时,会阻塞等待计数器归零。其他线程通过调用 countDown() 方法减少计数器值。

典型应用场景

  1. 多任务并行执行:等待所有任务完成后执行汇总操作
  2. 资源预热:等待所有资源初始化完成后开始业务流程
  3. 性能测试:控制并发请求同时发起

实现细节

java 复制代码
public class CountDownLatchExample {
    private static final int THREAD_COUNT = 5;

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(THREAD_COUNT);

        for (int i = 0; i < THREAD_COUNT; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " 开始执行");
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + " 执行完成");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    latch.countDown();
                }
            }).start();
        }

        latch.await();
        System.out.println("所有任务执行完毕");
    }
}

扩展应用:实现超时等待

java 复制代码
boolean allTasksDone = latch.await(5, TimeUnit.SECONDS);
if (!allTasksDone) {
    // 处理超时逻辑
}

三、CyclicBarrier:循环屏障

核心原理

CyclicBarrier 允许一组线程在某个屏障点(barrier)等待彼此。当所有线程到达屏障点后,屏障打开,所有线程继续执行。与 CountDownLatch 不同,CyclicBarrier 可以重复使用。

典型应用场景

  1. 迭代计算:多线程迭代计算,每轮计算后等待所有线程同步
  2. 数据分片处理:多个线程处理不同数据分片,完成后汇总结果
  3. 并发测试:控制多个线程同时开始执行

实现细节

java 复制代码
public class CyclicBarrierExample {
    private static final int THREAD_COUNT = 3;

    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT, () -> {
            System.out.println("所有线程到达屏障点");
        });

        for (int i = 0; i < THREAD_COUNT; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " 到达屏障点");
                    barrier.await();
                    System.out.println(Thread.currentThread().getName() + " 继续执行");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

高级特性:动态调整参与线程数

java 复制代码
// 移除一个参与线程
barrier.reset();

四、Semaphore:信号量

核心原理

Semaphore 维护一组许可证(permit),通过控制许可证的数量来限制对共享资源的并发访问。线程获取许可证后才能执行操作,释放后其他线程才能获取。

典型应用场景

  1. 限流控制:限制同时访问数据库的连接数
  2. 资源池管理:管理线程池中的线程数量
  3. 公平性控制:保证线程获取许可证的顺序

实现细节

java 复制代码
public class SemaphoreExample {
    private static final int MAX_CONCURRENT = 2;

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(MAX_CONCURRENT);

        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + " 获得许可证");
                    Thread.sleep(2000);
                    System.out.println(Thread.currentThread().getName() + " 释放许可证");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                }
            }).start();
        }
    }
}

扩展应用:公平性与非公平性

java 复制代码
// 公平性 Semaphore
Semaphore fairSemaphore = new Semaphore(MAX_CONCURRENT, true);

五、工具类对比与选择

工具类 核心功能 典型使用场景 线程安全机制
CountDownLatch 等待多个线程完成 任务汇总、资源初始化 计数器原子操作
CyclicBarrier 线程间同步屏障 迭代计算、数据分片处理 重入锁 + 条件变量
Semaphore 控制资源访问并发数 限流、资源池管理 基于 AQS 的状态机

六、并发工具类的性能优化

1. 减少上下文切换

java 复制代码
// 使用带超时的 await 方法避免无限阻塞
boolean acquired = semaphore.tryAcquire(5, TimeUnit.SECONDS);

2. 批量操作优化

java 复制代码
// 一次获取多个许可证
semaphore.acquire(3);

3. 结合线程池使用

java 复制代码
ExecutorService executor = Executors.newFixedThreadPool(10);
CyclicBarrier barrier = new CyclicBarrier(10);

for (int i = 0; i < 10; i++) {
    executor.submit(() -> {
        try {
            barrier.await();
            // 业务逻辑
        } catch (Exception e) {
            // 处理异常
        }
    });
}

七、实践案例:分布式系统中的锁机制

需求背景

某分布式系统需要控制同一资源的并发访问,确保最多 3 个节点同时操作。

实现方案

java 复制代码
public class DistributedLock {
    private static final int MAX_CONCURRENT = 3;
    private static final Semaphore semaphore = new Semaphore(MAX_CONCURRENT);

    public static void operateResource() {
        try {
            semaphore.acquire();
            // 操作资源
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            semaphore.release();
        }
    }
}

优化方案

java 复制代码
// 使用可中断锁避免死锁
semaphore.acquire(2); // 一次获取多个许可证

八、并发工具类的陷阱与解决方案

1. 线程泄漏

java 复制代码
// 使用 try - finally 确保释放资源
try {
    semaphore.acquire();
    // 业务逻辑
} finally {
    semaphore.release();
}

2. 虚假唤醒

java 复制代码
// 在循环中检查条件
while (!semaphore.tryAcquire()) {
    // 处理等待逻辑
}

3. 性能瓶颈

java 复制代码
// 使用公平性策略
Semaphore fairSemaphore = new Semaphore(MAX_CONCURRENT, true);

九、总结

Java 并发工具类为多线程编程提供了强大的支持,合理使用这些工具可以显著提升代码的可读性和性能。在实际开发中,需要根据具体场景选择合适的工具类:

  • CountDownLatch 适合"多对一"的同步场景
  • CyclicBarrier 适合"多对多"的循环同步场景
  • Semaphore 适合资源访问控制场景

通过结合线程池、原子类等工具,可以构建出高效、稳定的并发系统。同时,需要注意工具类的使用陷阱,避免因误用导致的性能问题或死锁。

相关推荐
葫芦和十三2 小时前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp3 小时前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑3 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯4 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan6 小时前
多Agent之间的区别
后端
青石路8 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充8 小时前
1.面向对象设计思想
后端
IT_陈寒9 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro9 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端
要阿尔卑斯吗9 小时前
提示词优化启示:为什么“按顺序输出“比“关键度评分“更有效
后端