跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava算法

Java 多线程并发编程:并发容器与线程协作实战

Java 多线程并发编程涉及常用并发容器特性与线程协作原理。文章详解 ConcurrentHashMap 分段锁与 CAS 机制、CopyOnWriteArrayList 写时复制策略、BlockingQueue 阻塞队列实现生产者消费者模式。同时介绍 CountDownLatch、CyclicBarrier、Semaphore 等工具类在任务汇总、数据分片及接口限流场景的应用。通过实战案例展示如何结合并发组件解决高并发下的数据安全与性能问题,提供选型建议以优化系统稳定性。

剑仙发布于 2026/3/26更新于 2026/6/216 浏览
Java 多线程并发编程:并发容器与线程协作实战

Java 多线程并发编程:并发容器与线程协作实战

图片

学习目标:掌握 JAVA 中常用并发容器的特性与适用场景,理解线程间协作的核心原理,能够运用并发容器和协作工具解决实际并发问题。 学习重点:并发容器与普通容器的区别、ConcurrentHashMap 核心原理、CountDownLatch/CyclicBarrier/Semaphore 的使用、生产者消费者模式实现。

1.1 为什么需要并发容器?

在多线程场景下,普通的集合容器(如 HashMap、ArrayList)是线程不安全的。多个线程同时对其进行读写操作时,会导致数据错乱、ConcurrentModificationException 异常等问题。

⚠️ 注意事项:即使使用 Collections.synchronizedXXX() 方法包装普通容器,也只是通过 synchronized 实现简单的加锁。这种方式锁粒度较粗,并发性能较低。

✅ 核心结论:并发容器是 JAVA 为多线程场景设计的高性能容器。它们通过细粒度锁或无锁算法实现线程安全,能够在保证数据一致性的同时,大幅提升并发访问效率。

1.2 常用并发容器详解

1.2.1 ConcurrentHashMap:高效并发哈希表

ConcurrentHashMap 是 HashMap 的并发安全版本,是日常开发中使用频率最高的并发容器。

1.2.1.1 核心特点
  1. 分段锁(JDK1.7)→ CAS + 同步锁(JDK1.8)
    • JDK1.7 采用分段锁机制,将数据分成多个 Segment。每个 Segment 独立加锁,不同 Segment 的操作互不阻塞。
    • JDK1.8 抛弃分段锁,采用 CAS + synchronized 实现。锁粒度缩小到单个 Node 节点,并发性能进一步提升。
  2. 支持高并发读写:读操作无锁(通过 volatile 保证可见性),写操作仅锁定当前节点,不会阻塞其他操作。
  3. 不允许 null 键值:与 HashMap 不同,ConcurrentHashMap 的键和值都不能为 null,避免歧义。
1.2.1.2 核心方法使用示例
import java.util.concurrent.ConcurrentHashMap;

/**
 * ConcurrentHashMap 实战示例
 */
public class ConcurrentHashMapDemo {
         InterruptedException {
        ConcurrentHashMap<String, Integer> map =  <>();
        
        map.put(, );
        map.put(, );
        map.put(, );
        
            (() -> {
             (   ; i < ; i++) {
                
                map.computeIfPresent(, (k, v) -> v + );
                System.out.println(Thread.currentThread().getName() +  + map.get());
            }
        }, );
            (() -> {
             (   ; i < ; i++) {
                map.computeIfPresent(, (k, v) -> v + );
                System.out.println(Thread.currentThread().getName() +  + map.get());
            }
        }, );
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        
        map.forEach((k, v) -> System.out.println(k +  + v));
    }
}
public
static
void
main
(String[] args)
throws
new
ConcurrentHashMap
// 1. 插入数据
"apple"
10
"banana"
20
"orange"
30
// 2. 并发修改数据
Thread
thread1
=
new
Thread
for
int
i
=
0
5
// 原子性操作:获取并增加
"apple"
1
" : apple = "
"apple"
"线程 1"
Thread
thread2
=
new
Thread
for
int
i
=
0
5
"apple"
1
" : apple = "
"apple"
"线程 2"
// 3. 遍历数据
" : "
1.2.1.3 适用场景
  • 高并发下的键值对存储场景,如缓存、用户会话存储。
  • 读多写少的业务场景,能充分发挥其无锁读的性能优势。
1.2.2 CopyOnWriteArrayList:写时复制数组

CopyOnWriteArrayList 是 ArrayList 的并发安全版本,核心思想是写时复制。

1.2.2.1 核心原理

① 📝 写操作:当执行添加、删除、修改操作时,会复制一份新的数组。在新数组上完成操作后,再将原数组的引用指向新数组。 ② 🔍 读操作:直接读取原数组,无需加锁,保证了读操作的高效性。 ③ ⚠️ 数据一致性:写操作是原子性的,读操作可能读取到旧数据。该容器适用于读多写少的场景。

1.2.2.2 使用示例
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * CopyOnWriteArrayList 实战示例
 */
public class CopyOnWriteArrayListDemo {
    public static void main(String[] args) throws InterruptedException {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        list.add("Java");
        list.add("Python");
        list.add("Go");
        // 1. 并发遍历与添加
        Thread writeThread = new Thread(() -> {
            list.add("C++");
            System.out.println(Thread.currentThread().getName() + " 添加元素:C++");
        }, "写线程");
        Thread readThread = new Thread(() -> {
            Iterator<String> iterator = list.iterator();
            while (iterator.hasNext()) {
                System.out.println(Thread.currentThread().getName() + " 遍历元素:" + iterator.next());
                // 模拟延迟
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "读线程");
        readThread.start();
        Thread.sleep(50);
        writeThread.start();
        readThread.join();
        writeThread.join();
        // 2. 最终遍历结果
        System.out.println("最终集合元素:" + list);
    }
}
1.2.2.3 适用场景
  • 读操作远多于写操作的场景,如系统配置读取、日志记录列表。
  • 不要求数据实时一致性的场景,允许读取到旧数据。
1.2.3 BlockingQueue:阻塞队列

BlockingQueue 是一个支持阻塞操作的队列,是实现生产者消费者模式的核心工具。

1.2.3.1 核心特性
  • 入队阻塞:当队列已满时,入队操作会阻塞,直到队列有空闲空间。
  • 出队阻塞:当队列为空时,出队操作会阻塞,直到队列中有元素。
  • 常用实现类:ArrayBlockingQueue(数组实现,有界)、LinkedBlockingQueue(链表实现,可选有界)、SynchronousQueue(同步队列,无容量)。
1.2.3.2 核心方法对比
方法类型抛出异常返回特殊值阻塞超时退出
入队add(e)offer(e)put(e)offer(e, time, unit)
出队remove()poll()take()poll(time, unit)
检查element()peek()--
1.2.3.3 使用示例
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

/**
 * BlockingQueue 实战示例
 */
public class BlockingQueueDemo {
    // 定义有界阻塞队列,容量为 3
    private static final BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);

    public static void main(String[] args) {
        // 生产者线程
        new Thread(() -> {
            String[] products = {"产品 A", "产品 B", "产品 C", "产品 D"};
            for (String product : products) {
                try {
                    System.out.println(Thread.currentThread().getName() + " 生产:" + product);
                    queue.put(product);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "生产者").start();
        // 消费者线程
        new Thread(() -> {
            while (true) {
                try {
                    String product = queue.take();
                    System.out.println(Thread.currentThread().getName() + " 消费:" + product);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "消费者").start();
    }
}

1.3 线程协作工具类

在复杂的并发场景中,需要多个线程协同完成任务。JAVA 提供了 CountDownLatch、CyclicBarrier、Semaphore 等工具类,简化线程协作的开发。

1.3.1 CountDownLatch:倒计时门闩

CountDownLatch 允许一个或多个线程等待其他线程完成操作后,再继续执行。

1.3.1.1 核心原理
  1. 初始化时指定计数器值,该值代表需要等待的线程数量。
  2. 每个线程完成任务后,调用 countDown() 方法,计数器值减 1。
  3. 主线程调用 await() 方法,会阻塞直到计数器值变为 0。
  4. 计数器值不可重置,CountDownLatch 只能使用一次。
1.3.1.2 实战案例:多线程任务汇总
import java.util.concurrent.CountDownLatch;

/**
 * CountDownLatch 实战:多线程数据统计
 */
public class CountDownLatchDemo {
    // 定义计数器,需要等待 3 个任务完成
    private static final CountDownLatch latch = new CountDownLatch(3);

    public static void main(String[] args) throws InterruptedException {
        System.out.println("主线程:开始执行数据统计任务");
        // 任务 1:用户数据统计
        new Thread(() -> {
            try {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + ":用户数据统计完成");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                latch.countDown();
            }
        }, "任务线程 1").start();
        // 任务 2:订单数据统计
        new Thread(() -> {
            try {
                Thread.sleep(1500);
                System.out.println(Thread.currentThread().getName() + ":订单数据统计完成");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                latch.countDown();
            }
        }, "任务线程 2").start();
        // 任务 3:商品数据统计
        new Thread(() -> {
            try {
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + ":商品数据统计完成");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                latch.countDown();
            }
        }, "任务线程 3").start();
        // 等待所有任务完成
        latch.await();
        System.out.println("主线程:所有数据统计完成,生成汇总报表");
    }
}
1.3.1.3 适用场景
  • 主线程等待多个子线程完成初始化任务,如系统启动时加载配置、连接资源。
  • 批量任务执行场景,需要等待所有任务完成后进行结果汇总。
1.3.2 CyclicBarrier:循环栅栏

CyclicBarrier 允许一组线程相互等待,直到所有线程都到达某个屏障点后,再继续执行。

1.3.2.1 核心原理
  1. 初始化时指定参与线程数量和屏障动作。屏障动作是所有线程到达后执行的任务。
  2. 每个线程到达屏障点时,调用 await() 方法,会阻塞直到所有线程都到达。
  3. 所有线程到达后,执行屏障动作,然后重置计数器。CyclicBarrier可以重复使用。
1.3.2.2 实战案例:多线程数据分片处理
import java.util.concurrent.CyclicBarrier;

/**
 * CyclicBarrier 实战:数据分片处理
 */
public class CyclicBarrierDemo {
    // 定义循环栅栏,4 个线程参与,所有线程到达后执行屏障动作
    private static final CyclicBarrier barrier = new CyclicBarrier(4, () -> {
        System.out.println("屏障动作:所有分片数据处理完成,开始合并结果");
    });

    public static void main(String[] args) {
        // 模拟 4 个数据分片
        for (int i = 0; i < 4; i++) {
            int shard = i + 1;
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + ":处理分片" + shard + "数据");
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + ":分片" + shard + "处理完成,等待其他线程");
                    // 到达屏障点
                    barrier.await();
                    System.out.println(Thread.currentThread().getName() + ":结果合并完成,继续后续任务");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, "分片线程" + shard).start();
        }
    }
}
1.3.2.3 CountDownLatch vs CyclicBarrier
特性CountDownLatchCyclicBarrier
计数器重置不可重置,只能用一次可重置,可重复使用
等待方向一个或多个线程等待其他线程多个线程相互等待
核心场景主线程等待子线程完成线程组协同完成任务
1.3.3 Semaphore:信号量

Semaphore 用于控制同时访问特定资源的线程数量,通过许可证机制实现资源限流。

1.3.3.1 核心原理
  1. 初始化时指定许可证数量,代表允许同时访问资源的线程数。
  2. 线程访问资源前,调用 acquire() 方法获取许可证。无可用许可证时,线程会阻塞。
  3. 线程释放资源后,调用 release() 方法归还许可证。
  4. 许可证数量可以动态调整,支持公平/非公平模式。
1.3.3.2 实战案例:接口限流
import java.util.concurrent.Semaphore;

/**
 * Semaphore 实战:接口限流
 */
public class SemaphoreDemo {
    // 定义信号量,允许 3 个线程同时访问
    private static final Semaphore semaphore = new Semaphore(3);

    // 模拟接口方法
    public static void apiInvoke(String threadName) throws InterruptedException {
        // 获取许可证
        semaphore.acquire();
        try {
            System.out.println(threadName + ":获取许可证,开始调用接口");
            Thread.sleep(1000);
            System.out.println(threadName + ":接口调用完成");
        } finally {
            // 归还许可证
            semaphore.release();
            System.out.println(threadName + ":归还许可证,当前可用许可证:" + semaphore.availablePermits());
        }
    }

    public static void main(String[] args) {
        // 模拟 10 个线程并发调用接口
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                try {
                    apiInvoke("线程" + finalI);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
1.3.3.3 适用场景
  • 接口限流,控制并发访问数,防止系统过载。
  • 资源池访问控制,如数据库连接池、线程池的资源分配。

1.4 实战案例:基于并发容器实现生产者消费者模式

生产者消费者模式是并发编程中的经典模式。它通过阻塞队列解耦生产者和消费者,平衡生产和消费速度。

1.4.1 需求分析
  1. 生产者线程生产商品,将商品放入阻塞队列。
  2. 消费者线程从阻塞队列中取出商品进行消费。
  3. 当队列满时,生产者阻塞;当队列空时,消费者阻塞。
  4. 支持多个生产者和多个消费者并发执行。
1.4.2 代码实现
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * 实战:基于 BlockingQueue 实现生产者消费者模式
 */
public class ProducerConsumerPattern {
    // 定义有界阻塞队列,容量为 5
    private static final BlockingQueue<Product> queue = new ArrayBlockingQueue<>(5);

    // 商品实体类
    static class Product {
        private String id;
        private String name;

        public Product(String id, String name) {
            this.id = id;
            this.name = name;
        }

        @Override
        public String toString() {
            return "Product{id='" + id + "', name='" + name + "'}";
        }
    }

    // 生产者类
    static class Producer implements Runnable {
        private String producerName;

        public Producer(String producerName) {
            this.producerName = producerName;
        }

        @Override
        public void run() {
            int count = 1;
            while (true) {
                try {
                    Product product = new Product("P" + count, "商品" + count);
                    queue.put(product);
                    System.out.println(producerName + " 生产:" + product + ",队列当前大小:" + queue.size());
                    count++;
                    // 模拟生产耗时
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

    // 消费者类
    static class Consumer implements Runnable {
        private String consumerName;

        public Consumer(String consumerName) {
            this.consumerName = consumerName;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Product product = queue.take();
                    System.out.println(consumerName + " 消费:" + product + ",队列当前大小:" + queue.size());
                    // 模拟消费耗时
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

    public static void main(String[] args) {
        // 启动 2 个生产者线程
        new Thread(new Producer("生产者 1")).start();
        new Thread(new Producer("生产者 2")).start();
        // 启动 3 个消费者线程
        new Thread(new Consumer("消费者 1")).start();
        new Thread(new Consumer("消费者 2")).start();
        new Thread(new Consumer("消费者 3")).start();
    }
}
1.4.3 运行结果分析
  • 当队列满时,生产者线程会阻塞,直到消费者消费商品腾出空间。
  • 当队列空时,消费者线程会阻塞,直到生产者生产新的商品。
  • 多个生产者和消费者可以并发执行,系统运行稳定,不会出现数据错乱。

✅ 实战结论:基于 BlockingQueue 实现生产者消费者模式,无需手动加锁和控制线程状态。这种方式代码简洁、性能稳定,是并发编程中的优选方案。

1.5 并发容器与协作工具选型建议

工具类型具体实现核心优势适用场景
并发 MapConcurrentHashMap高并发读写、细粒度锁缓存存储、键值对数据共享
并发 ListCopyOnWriteArrayList读操作无锁、性能高读多写少、配置列表
阻塞队列ArrayBlockingQueue/LinkedBlockingQueue自动阻塞、解耦生产者消费者任务队列、消息传递
线程协作CountDownLatch主线程等待子线程完成任务汇总、系统启动
线程协作CyclicBarrier线程组相互等待、可重复使用数据分片处理、批量任务
限流工具Semaphore控制并发访问数接口限流、资源池管理

1.6 本章小结

💡 本章重点讲解了 JAVA 中常用的并发容器和线程协作工具。包括 ConcurrentHashMap、CopyOnWriteArrayList、BlockingQueue 的核心原理与使用方法,以及 CountDownLatch、CyclicBarrier、Semaphore 三个协作工具的实战场景。 💡 通过生产者消费者模式的完整案例,掌握了如何结合并发容器和协作工具,实现高效、安全的并发编程。 ✅ 并发容器和协作工具是 JAVA 并发编程的核心组件。合理选择和使用这些工具,能够大幅降低并发编程的复杂度,提升系统的稳定性和性能。

目录

  1. Java 多线程并发编程:并发容器与线程协作实战
  2. 1.1 为什么需要并发容器?
  3. 1.2 常用并发容器详解
  4. 1.2.1 ConcurrentHashMap:高效并发哈希表
  5. 1.2.1.1 核心特点
  6. 1.2.1.2 核心方法使用示例
  7. 1.2.1.3 适用场景
  8. 1.2.2 CopyOnWriteArrayList:写时复制数组
  9. 1.2.2.1 核心原理
  10. 1.2.2.2 使用示例
  11. 1.2.2.3 适用场景
  12. 1.2.3 BlockingQueue:阻塞队列
  13. 1.2.3.1 核心特性
  14. 1.2.3.2 核心方法对比
  15. 1.2.3.3 使用示例
  16. 1.3 线程协作工具类
  17. 1.3.1 CountDownLatch:倒计时门闩
  18. 1.3.1.1 核心原理
  19. 1.3.1.2 实战案例:多线程任务汇总
  20. 1.3.1.3 适用场景
  21. 1.3.2 CyclicBarrier:循环栅栏
  22. 1.3.2.1 核心原理
  23. 1.3.2.2 实战案例:多线程数据分片处理
  24. 1.3.2.3 CountDownLatch vs CyclicBarrier
  25. 1.3.3 Semaphore:信号量
  26. 1.3.3.1 核心原理
  27. 1.3.3.2 实战案例:接口限流
  28. 1.3.3.3 适用场景
  29. 1.4 实战案例:基于并发容器实现生产者消费者模式
  30. 1.4.1 需求分析
  31. 1.4.2 代码实现
  32. 1.4.3 运行结果分析
  33. 1.5 并发容器与协作工具选型建议
  34. 1.6 本章小结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • OpenClaw WebUI 空白页问题及配置修复
  • 腾讯向 GitHub 发函要求下架 4000 多个微信聊天记录相关开源仓库
  • 阿里开源 Page-Agent:一行 JS 代码实现大模型前端 DOM 寄生
  • C++ 四种类型转换详解:static_cast、reinterpret_cast、const_cast 与 dynamic_cast
  • SpringBoot 开发环境搭建与配置
  • Spring Cloud Nacos 负载均衡策略与实践
  • LLM 继续预训练(Continue Pretrain)实践指南与经验总结
  • ABP vNext WebAPI 应用开发指南
  • Python 职业前景与技能应用场景解析
  • 兼容ie和firefox的获取html元素自定义属性的方法
  • 基于 Leaflet 的 WebGIS 省域区县天气可视化实战
  • C 语言快速排序详解:从基础到非递归实现
  • WebVOWL 本体可视化工具安装与使用指南
  • FT8440AD 非隔离 12V350mA 电源芯片方案及 SDH8302 替代
  • WSL 2 安装 Ubuntu 24.04 及系统迁移至非系统盘
  • Cursor 与 Claude Code 对比:开发者如何选择 AI 编程工具
  • Android WebRTC 源码解析:从媒体流处理到实时通信优化
  • Docker Compose rm 命令详解与使用指南
  • 毕业论文降低 AI 检测率的原理与实操方法
  • 大语言模型词表裁剪方法与实践

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online