当前位置: 首页 > news >正文

面试系列 - Java 并发容器详解

Java 并发容器是一组用于在多线程环境下安全访问和操作数据的数据结构。它们提供了线程安全的集合和映射,使开发者能够更轻松地处理并发编程问题。

一、Java并发容器

  1. ConcurrentHashMap: 它是一个高效的并发哈希表,支持多线程并发操作而不需要显式的同步。适用于高并发读和写操作的场景。

  2. CopyOnWriteArrayList :这个容器支持在迭代期间进行并发写操作。当需要在读操作频繁而写操作较少的场景中使用。

  3. ConcurrentLinkedQueue 和 ConcurrentLinkedDeque: 这些是非阻塞队列实现,适用于高吞吐量的并发队列操作。通常用于任务调度、工作队列等场景。

  4. BlockingQueue: 它是一组阻塞队列的接口,如 LinkedBlockingQueue、ArrayBlockingQueue、PriorityBlockingQueue 等。这些队列提供了在队列为空或满时阻塞线程的能力,适用于生产者-消费者问题和任务调度等。

  5. Semaphore 和 CountDownLatch: 虽然不是容器,但它们是并发编程中常用的同步工具。

二、ConcurrentHashMap 

ConcurrentHashMap 是 Java 中的一个线程安全的哈希表(散列表)实现,它允许多个线程同时读取数据,而不需要显式的同步操作,并且支持高并发的读和写操作。ConcurrentHashMap 的主要特点包括:

  1. 并发读取: 多个线程可以同时读取数据,不会被阻塞,这使得 ConcurrentHashMap 在读多写少的场景下非常高效。

  2. 分段锁: ConcurrentHashMap 内部使用了分段锁机制,将数据分成多个段(Segment),每个段上都有一个锁,不同线程可以同时访问不同段的数据,从而提高并发性能。

  3. 线程安全写入: 写操作(如插入、更新、删除)仍然是线程安全的,但它不会锁住整个数据结构,而只锁住相关的段,使得其他线程仍然可以访问不同段的数据。

  4. 弱一致性: ConcurrentHashMap 提供了一定程度的弱一致性,因为它不保证在某一时刻数据的完全一致性,但保证了线程的安全性。

  5. 扩展性: ConcurrentHashMap 具有较好的扩展性,可以通过调整初始容量和负载因子来优化性能。

  6. 迭代支持: ConcurrentHashMap 提供了安全的迭代器,它可以在遍历时支持并发写操作。

     ConcurrentHashMap用法示例

import java.util.concurrent.ConcurrentHashMap;public class ConcurrentHashMapExample {public static void main(String[] args) {ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();// 插入数据map.put("one", 1);map.put("two", 2);map.put("three", 3);// 获取数据int value = map.get("two");System.out.println("Value for key 'two': " + value);// 删除数据map.remove("three");// 迭代数据for (String key : map.keySet()) {int val = map.get(key);System.out.println(key + ": " + val);}}
}

虽然 ConcurrentHashMap 支持高并发读取和写入,但在一些特定情况下仍然需要考虑同步和竞态条件问题。

三、CopyOnWriteArrayList

CopyOnWriteArrayList 是 Java 集合框架中的一种线程安全的并发容器,它提供了一种特殊的并发策略,适用于读多写少的场景。与普通的 ArrayList 不同,CopyOnWriteArrayList 在写操作时不对原有数据进行修改,而是创建一个新的副本,这样可以避免读操作与写操作之间的竞态条件,保证读操作的线程安全。

主要特点和用途包括:

  1. 线程安全的读取: 多个线程可以同时读取数据,而不需要显式的同步操作。这使得 CopyOnWriteArrayList 在读多写少的场景下非常高效。

  2. 写入时复制(Copy-On-Write): 写入操作会创建一个新的副本,而不是修改原有数据。这确保了写操作不会影响正在进行的读操作,保证了读操作的一致性。原有的数据在写入完成后会被废弃。

  3. 高效的迭代: CopyOnWriteArrayList 提供了安全的迭代器,可以在迭代集合时进行并发写操作,不会抛出 ConcurrentModificationException 异常。

  4. 适用于不频繁写入的场景: 由于写操作涉及复制数据,因此 CopyOnWriteArrayList 适用于写操作不频繁、但读操作较多的情况,如事件监听器列表、观察者模式等。

 下面是一个简单示例,展示了如何使用 CopyOnWriteArrayList

import java.util.concurrent.CopyOnWriteArrayList;public class CopyOnWriteArrayListExample {public static void main(String[] args) {CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();// 插入数据list.add("one");list.add("two");list.add("three");// 获取数据String value = list.get(1);System.out.println("Value at index 1: " + value);// 迭代数据for (String item : list) {System.out.println(item);}// 写操作不影响正在进行的读操作list.add("four");// 迭代数据(包含新添加的元素)for (String item : list) {System.out.println(item);}}
}

四、BlockingQueue

BlockingQueue 是 Java 并发编程中的一个重要接口,它表示一种线程安全的阻塞队列。阻塞队列在多线程编程中非常有用,因为它提供了一种线程安全的方式来实现生产者-消费者模型和其他协作模式。

BlockingQueue 接口继承自 Queue 接口,它定义了一组用于插入、删除和检查元素的方法,但与普通的队列不同,BlockingQueue 的方法具有阻塞特性,这意味着当队列为空时,从队列中取元素的操作会被阻塞,直到有元素可用;当队列已满时,向队列中插入元素的操作也会被阻塞,直到有空间可用。

常见的 BlockingQueue 实现包括:

  1. LinkedBlockingQueue: 基于链表的阻塞队列,可以设置容量,当队列为空或已满时,操作会被阻塞。

  2. ArrayBlockingQueue: 基于数组的阻塞队列,需要指定容量,当队列为空或已满时,操作会被阻塞。

  3. PriorityBlockingQueue: 基于优先级的阻塞队列,元素会按照优先级进行排序,不需要容量限制。

  4. DelayQueue: 用于存放实现了 Delayed 接口的元素,元素只能在延迟时间过后才能被取出。

  5. LinkedTransferQueue: 支持多个生产者和消费者的队列,具有高吞吐量。

  6. SynchronousQueue: 一种特殊的阻塞队列,它没有容量,用于直接传递数据的通信。

使用 BlockingQueue 可以轻松实现一些经典的多线程协作模式,例如生产者-消费者模型。以下是一个简单示例,演示了如何使用 LinkedBlockingQueue 实现生产者-消费者模型:

import java.util.concurrent.*;public class ProducerConsumerExample {public static void main(String[] args) {BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(10);// 生产者线程Thread producer = new Thread(() -> {try {for (int i = 1; i <= 10; i++) {queue.put(i);System.out.println("Produced: " + i);Thread.sleep(100);}} catch (InterruptedException e) {Thread.currentThread().interrupt();}});// 消费者线程Thread consumer = new Thread(() -> {try {for (int i = 1; i <= 10; i++) {int value = queue.take();System.out.println("Consumed: " + value);Thread.sleep(200);}} catch (InterruptedException e) {Thread.currentThread().interrupt();}});producer.start();consumer.start();}
}

在上述示例中,生产者线程不断往队列中放入数据,而消费者线程则不断从队列中取出数据,它们通过阻塞队列实现了线程间的协作。这种模型可以用于处理多个生产者和消费者的情况,并确保线程安全。

BlockingQueue 是并发编程中的一个重要工具,可以简化线程之间的通信和同步,减少了开发者需要处理的低级细节。根据具体的需求,选择合适的 BlockingQueue 实现非常重要。

五、Semaphore  

Semaphore 是 Java 并发编程中的一个同步工具,用于控制同时访问某个资源的线程数量。它允许多个线程并发访问共享资源,但可以限制同时访问的线程数目,从而实现资源的有序分配。

Semaphore 主要有两个重要的操作:

  1. acquire(): 当一个线程希望获取资源时,它调用 acquire() 方法。如果有可用的资源,线程将获得许可,并继续执行。如果没有可用的资源,线程将被阻塞,直到有资源可用为止。

  2. release(): 当一个线程使用完资源时,它调用 release() 方法来释放资源。这会导致信号量的许可数增加,从而允许其他等待资源的线程获得许可并继续执行。

Semaphore 可以用于以下一些典型的场景:

  1. 控制资源访问数量: 限制同时访问某个共享资源的线程数量,例如数据库连接池的控制。

  2. 实现有界容器: 可以用 Semaphore 来实现有界的集合,例如有界队列。

  3. 实现并发任务控制: 控制并发任务的数量,例如控制同时运行的线程数量。

下面是一个简单示例,演示了如何使用 Semaphore 来控制同时执行的线程数量:

 

import java.util.concurrent.Semaphore;public class SemaphoreExample {public static void main(String[] args) {// 创建一个Semaphore,限制最多同时有两个线程访问共享资源Semaphore semaphore = new Semaphore(2);// 创建多个线程for (int i = 1; i <= 5; i++) {Thread thread = new Thread(() -> {try {System.out.println(Thread.currentThread().getName() + " is waiting for a permit.");semaphore.acquire();System.out.println(Thread.currentThread().getName() + " has acquired a permit.");Thread.sleep(2000); // 模拟线程在共享资源上的操作} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {System.out.println(Thread.currentThread().getName() + " is releasing a permit.");semaphore.release();}});thread.start();}}
}

在上述示例中,我们创建了一个 Semaphore,并限制最多同时有两个线程可以获得许可。多个线程尝试获取许可,但只有两个线程能够同时执行,其余的线程会被阻塞,直到有许可可用。

Semaphore 是一个非常有用的同步工具,可用于管理共享资源的并发访问,控制线程的并发数量,以及其他需要有序控制的场景。

六、CountDownLatch

CountDownLatch 是 Java 并发编程中的一个同步工具类,它用于实现一种等待多个线程完成任务的场景。CountDownLatch 通过一个计数器来实现,计数器的值初始化为一个正整数,然后多个线程执行任务,每个任务执行完毕后,计数器的值减一,当计数器的值变为零时,等待的线程可以继续执行。

CountDownLatch 主要有两个重要的方法:

  1. countDown() 每个任务执行完毕后调用这个方法来减小计数器的值。通常在任务的最后一步调用。

  2. await() 等待计数器的值变为零,一直阻塞当前线程,直到计数器值为零才继续执行。

CountDownLatch 可以用于以下一些典型的场景:

  1. 多个线程协作完成任务: 主线程等待多个子线程完成任务后再继续执行。

  2. 控制并发任务的开始: 多个线程等待某个共同的事件发生后同时开始执行。

  3. 统计多个线程的执行时间: 可以用于测量多个线程完成任务所花费的总时间。

下面是一个简单示例,演示了如何使用 CountDownLatch 来等待多个线程完成任务:

import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {int numThreads = 3;CountDownLatch latch = new CountDownLatch(numThreads);for (int i = 0; i < numThreads; i++) {Thread thread = new Thread(() -> {try {// 模拟线程执行任务Thread.sleep(2000);System.out.println(Thread.currentThread().getName() + " has completed.");} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {latch.countDown(); // 任务执行完毕,减少计数器}});thread.start();}// 等待所有线程完成latch.await();System.out.println("All threads have completed.");}
}

在上述示例中,我们创建了一个 CountDownLatch,初始化计数器值为 3(表示有三个线程需要等待)。然后创建了三个线程,每个线程执行完毕后调用 countDown() 来减小计数器的值。主线程使用 await() 来等待计数器的值变为零,一旦所有线程都执行完毕,主线程继续执行。

CountDownLatch 是一个常用的同步工具,用于协调多个线程的执行,实现任务的并发控制。

相关文章:

面试系列 - Java 并发容器详解

Java 并发容器是一组用于在多线程环境下安全访问和操作数据的数据结构。它们提供了线程安全的集合和映射&#xff0c;使开发者能够更轻松地处理并发编程问题。 一、Java并发容器 ConcurrentHashMap&#xff1a; 它是一个高效的并发哈希表&#xff0c;支持多线程并发操作而不需…...

使用动态住宅代理还能带来哪些好处?

一、什么是动态住宅代理ip 动态住宅代理是一种代理技术&#xff0c;它利用代理服务器中转用户和目标服务器之间的网络流量&#xff0c;实现用户真实位置的屏蔽。代理提供商会有自己的ip大池子&#xff0c;当你通过代理服务器向网站发送请求时&#xff0c;服务器会从池子中选中…...

笙默考试管理系统-MyExamTest----codemirror(18)

笙默考试管理系统-MyExamTest----codemirror&#xff08;18&#xff09; 目录 一、 笙默考试管理系统-MyExamTest----codemirror 二、 笙默考试管理系统-MyExamTest----codemirror 三、 笙默考试管理系统-MyExamTest----codemirror 四、 笙默考试管理系统-MyExamTest---…...

TGA格式文件转材质

今天淘宝上买了一个美女的模型&#xff0c;是blender的源文件&#xff0c;上面说有fbx格式的。我用unity&#xff0c;所以觉得应该可以用。文件内容如下图&#xff1a; FBX文件夹打开后&#xff0c;内容如下图所示&#xff0c;当时就预感到可能没有色彩。 unity打开后果然发现只…...

IP应用场景查询API:深入了解网络用户行为的利器

前言 随着数字时代的不断发展&#xff0c;互联网已经成为人们生活的重要组成部分。而随着越来越多的业务和社交活动迁移到在线平台上&#xff0c;了解和理解网络用户行为变得至关重要。为了满足这个需求&#xff0c;IP 应用场景查询 API 崭露头角&#xff0c;成为深入了解网络…...

docker从零部署jenkins保姆级教程(上)

jenkins&#xff0c;基本是最常用的持续集成工具。在实际的工作中&#xff0c;后端研发一般没有jenkins的操作权限&#xff0c;只有一些查看权限&#xff0c;但是我们的代码是经过这个工具构建出来部署到服务器的&#xff0c;所以我觉着有必要了解一下这个工具的搭建过程以及简…...

2023数模A题——定日镜场的优化问题

A题——定日镜场的优化问题 思路&#xff1a;该题主要考察的几何知识和天文学知识&#xff0c;需要不同角度下的镜面和遮挡情况。 资料获取 问题1&#xff1a; 若将吸收塔建于该圆形定日镜场中心&#xff0c;定日镜尺寸均为 6 m6 m&#xff0c;安装高度均为 4 m&#xff0c;且…...

Container is running beyond memory limits

问题 Hadoop环境中&#xff0c;执行MapReduce程序或者Hive 任务时候&#xff0c;任务执行失败&#xff0c;提示内存不足。 Container is running 337869312B beyond the VIRTUAL’ memory limit.Current usage:295.8 NB of 1 GB physical memoryused;2.4 GB of 2.1 GB virtual…...

Java后端开发面试题——JVM虚拟机篇

目录 什么是程序计数器&#xff1f; 你能给我详细的介绍Java堆吗? 什么是虚拟机栈 1. 垃圾回收是否涉及栈内存&#xff1f; 2. 栈内存分配越大越好吗&#xff1f; 3. 方法内的局部变量是否线程安全&#xff1f; 4.什么情况下会导致栈内存溢出&#xff1f; 5.堆栈的区别…...

SpringMVC增删改查(CRUD)的实现

目录 前言 一、前期准备 1.pom.xml---依赖与插件的导入 2.jdbc.properties---数据库连接 3.log4j2.xml---日志文件 4.spring-mybatis 5.spring-context 6.spring-mvc 二、增删改查的实现 1.model与mapper层的生成 2.biz层 3.工具类 4.controller层 三、测试结果 总…...

智安网络|面临日益增长的安全威胁:云安全和零信任架构的重要性

随着云计算技术的快速发展和广泛应用&#xff0c;云安全和零信任架构变得愈发重要。在数字化时代&#xff0c;云计算技术得到了广泛的应用和推广。企业和组织借助云服务提供商的强大能力&#xff0c;实现了高效、灵活和可扩展的IT基础设施。然而&#xff0c;随着云环境的快速发…...

JVM常用调优策略

1、JVM调优的核心关注指标 调优之前首先我们要知道怎样才算是“优”&#xff0c;不能笼统的说我的程序性能很好&#xff0c;所以就需要有一个具体的指标来衡量性能情况&#xff0c;而在JVM里面衡量性能两个指标分别“吞吐量”和“停顿时间”。 吞吐量&#xff1a;程序运行过程…...

自动化防火墙放行目标域名IP

#!/bin/bash # 设置要获取IP地址的域名 domain"yourdomain.com"# 获取域名的IP地址 new_ip$(dig short A $domain)# 移除之前添加放行的IP地址&#xff08;通过备注找它的编号&#xff09; rule_number$(iptables -L INPUT -n --line-numbers -v | awk -v domain&quo…...

12.2RAC环境从RAC转为单机模式的问题处理

近期&#xff0c;在某用户的测试环境&#xff0c;需要将RAC转为单机模式&#xff0c;然后进行数据恢复&#xff1b;一开始只是将数据库软件通过make -f ins_rdbms.mk rac_off 和 make -f ins_rdbms.mk ioracle关闭RAC模式&#xff1b;然后在启动数据库&#xff08;sqlplus / a…...

Docker 中 jdk8容器里无法使用 JDK 的 jmap 等命令的问题

一、问题描述 项目部署在 CentOS 服务器上。项目偶尔会出现无响应的情况&#xff0c;这时理所当然要上去用 JDK 相关命令看看堆栈和GC等信息了。 进入 Java 程序所在容器&#xff1a;docekr-compose exec api bash&#xff0c;进入到 api 容器的 bash 终端。 jps 打印 Java …...

typeScript--[es6class类实现继承]

一.js中实现继承 // js实现继承 // 父类 function Father(name) {this.name namethis.say function () {console.log(this.name "在唱歌")} } var f new Father("逍遥的码农")// 子类 function Son(name) {Father.call(this, name) } Son.prototype …...

解决eclipse的报错:Must declare a named package because this compilation

刚安装完成eclipse&#xff0c; 创建类的时候报错 报错信息如下&#xff1a; 原因&#xff1a;新版本的ECLIPSE要求每一个类都必须定义在包里面 解决方法&#xff1a;创建类的时候指定类的名字&#xff0c;如下图&#xff1a;Package 里面填写ch3&#xff0c;表示包名 创建完成…...

linux sed常用各种操作大全

经常使用&#xff0c;但有些总记不全&#xff0c;有时候经常查找&#xff0c;这次全部捋清楚做备忘&#xff0c;有需要的小伙伴欢迎收藏起来哦&#xff01; 查、增、改、删一应俱全&#xff0c;非常详细&#xff01; 目录 一、查看 查看第2行 查看第2行到第3行 查看第1行、…...

通过 Keycloak 结合 OAuth2.0协议进行 Amazon API Gateway 鉴权

1. 简介 本文介绍了如何通过 Keycloak&#xff0c;并结合 Amazon API Gateway 内置的授权功能&#xff0c;完成对 Amazon 资源请求的鉴权过程。API Gateway 帮助开发者安全的的创建、发布、维护并管理 API 的访问。在中国区&#xff0c;由于Cognito 仍未上线&#xff0c;因此使…...

修复中间件log4j漏洞方案(直接更换漏洞jar包)

说明&#xff1a; 后台服务里面的log4j漏洞我们已经全部升级处理了&#xff0c;但是一些中间件镜像包里的log4j漏洞需要单独处理 解决办法以ElasticSearch7.6.2为例&#xff1a; 方法&#xff1a; &#xff08;1&#xff09;找到容器里面有哪些旧的log4j依赖包 &#xff08;…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?

在工业自动化持续演进的今天&#xff0c;通信网络的角色正变得愈发关键。 2025年6月6日&#xff0c;为期三天的华南国际工业博览会在深圳国际会展中心&#xff08;宝安&#xff09;圆满落幕。作为国内工业通信领域的技术型企业&#xff0c;光路科技&#xff08;Fiberroad&…...

大模型——基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程

基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程 下载安装Docker Docker官网:https://www.docker.com/ 自定义Docker安装路径 Docker默认安装在C盘,大小大概2.9G,做这行最忌讳的就是安装软件全装C盘,所以我调整了下安装路径。 新建安装目录:E:\MyS…...