面试系列 - Java 并发容器详解
Java 并发容器是一组用于在多线程环境下安全访问和操作数据的数据结构。它们提供了线程安全的集合和映射,使开发者能够更轻松地处理并发编程问题。
一、Java并发容器
-
ConcurrentHashMap: 它是一个高效的并发哈希表,支持多线程并发操作而不需要显式的同步。适用于高并发读和写操作的场景。
-
CopyOnWriteArrayList :这个容器支持在迭代期间进行并发写操作。当需要在读操作频繁而写操作较少的场景中使用。
-
ConcurrentLinkedQueue 和 ConcurrentLinkedDeque: 这些是非阻塞队列实现,适用于高吞吐量的并发队列操作。通常用于任务调度、工作队列等场景。
-
BlockingQueue: 它是一组阻塞队列的接口,如 LinkedBlockingQueue、ArrayBlockingQueue、PriorityBlockingQueue 等。这些队列提供了在队列为空或满时阻塞线程的能力,适用于生产者-消费者问题和任务调度等。
-
Semaphore 和 CountDownLatch: 虽然不是容器,但它们是并发编程中常用的同步工具。
二、ConcurrentHashMap
ConcurrentHashMap
是 Java 中的一个线程安全的哈希表(散列表)实现,它允许多个线程同时读取数据,而不需要显式的同步操作,并且支持高并发的读和写操作。ConcurrentHashMap
的主要特点包括:
-
并发读取: 多个线程可以同时读取数据,不会被阻塞,这使得
ConcurrentHashMap
在读多写少的场景下非常高效。 -
分段锁:
ConcurrentHashMap
内部使用了分段锁机制,将数据分成多个段(Segment),每个段上都有一个锁,不同线程可以同时访问不同段的数据,从而提高并发性能。 -
线程安全写入: 写操作(如插入、更新、删除)仍然是线程安全的,但它不会锁住整个数据结构,而只锁住相关的段,使得其他线程仍然可以访问不同段的数据。
-
弱一致性:
ConcurrentHashMap
提供了一定程度的弱一致性,因为它不保证在某一时刻数据的完全一致性,但保证了线程的安全性。 -
扩展性:
ConcurrentHashMap
具有较好的扩展性,可以通过调整初始容量和负载因子来优化性能。 -
迭代支持:
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
在写操作时不对原有数据进行修改,而是创建一个新的副本,这样可以避免读操作与写操作之间的竞态条件,保证读操作的线程安全。
主要特点和用途包括:
-
线程安全的读取: 多个线程可以同时读取数据,而不需要显式的同步操作。这使得
CopyOnWriteArrayList
在读多写少的场景下非常高效。 -
写入时复制(Copy-On-Write): 写入操作会创建一个新的副本,而不是修改原有数据。这确保了写操作不会影响正在进行的读操作,保证了读操作的一致性。原有的数据在写入完成后会被废弃。
-
高效的迭代:
CopyOnWriteArrayList
提供了安全的迭代器,可以在迭代集合时进行并发写操作,不会抛出ConcurrentModificationException
异常。 -
适用于不频繁写入的场景: 由于写操作涉及复制数据,因此
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
实现包括:
-
LinkedBlockingQueue: 基于链表的阻塞队列,可以设置容量,当队列为空或已满时,操作会被阻塞。
-
ArrayBlockingQueue: 基于数组的阻塞队列,需要指定容量,当队列为空或已满时,操作会被阻塞。
-
PriorityBlockingQueue: 基于优先级的阻塞队列,元素会按照优先级进行排序,不需要容量限制。
-
DelayQueue: 用于存放实现了
Delayed
接口的元素,元素只能在延迟时间过后才能被取出。 -
LinkedTransferQueue: 支持多个生产者和消费者的队列,具有高吞吐量。
-
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
主要有两个重要的操作:
-
acquire(): 当一个线程希望获取资源时,它调用
acquire()
方法。如果有可用的资源,线程将获得许可,并继续执行。如果没有可用的资源,线程将被阻塞,直到有资源可用为止。 -
release(): 当一个线程使用完资源时,它调用
release()
方法来释放资源。这会导致信号量的许可数增加,从而允许其他等待资源的线程获得许可并继续执行。
Semaphore
可以用于以下一些典型的场景:
-
控制资源访问数量: 限制同时访问某个共享资源的线程数量,例如数据库连接池的控制。
-
实现有界容器: 可以用
Semaphore
来实现有界的集合,例如有界队列。 -
实现并发任务控制: 控制并发任务的数量,例如控制同时运行的线程数量。
下面是一个简单示例,演示了如何使用 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
主要有两个重要的方法:
-
countDown()
: 每个任务执行完毕后调用这个方法来减小计数器的值。通常在任务的最后一步调用。 -
await()
: 等待计数器的值变为零,一直阻塞当前线程,直到计数器值为零才继续执行。
CountDownLatch
可以用于以下一些典型的场景:
-
多个线程协作完成任务: 主线程等待多个子线程完成任务后再继续执行。
-
控制并发任务的开始: 多个线程等待某个共同的事件发生后同时开始执行。
-
统计多个线程的执行时间: 可以用于测量多个线程完成任务所花费的总时间。
下面是一个简单示例,演示了如何使用 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 并发容器是一组用于在多线程环境下安全访问和操作数据的数据结构。它们提供了线程安全的集合和映射,使开发者能够更轻松地处理并发编程问题。 一、Java并发容器 ConcurrentHashMap: 它是一个高效的并发哈希表,支持多线程并发操作而不需…...

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

笙默考试管理系统-MyExamTest----codemirror(18)
笙默考试管理系统-MyExamTest----codemirror(18) 目录 一、 笙默考试管理系统-MyExamTest----codemirror 二、 笙默考试管理系统-MyExamTest----codemirror 三、 笙默考试管理系统-MyExamTest----codemirror 四、 笙默考试管理系统-MyExamTest---…...

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

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

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

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

Container is running beyond memory limits
问题 Hadoop环境中,执行MapReduce程序或者Hive 任务时候,任务执行失败,提示内存不足。 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虚拟机篇
目录 什么是程序计数器? 你能给我详细的介绍Java堆吗? 什么是虚拟机栈 1. 垃圾回收是否涉及栈内存? 2. 栈内存分配越大越好吗? 3. 方法内的局部变量是否线程安全? 4.什么情况下会导致栈内存溢出? 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层 三、测试结果 总…...

智安网络|面临日益增长的安全威胁:云安全和零信任架构的重要性
随着云计算技术的快速发展和广泛应用,云安全和零信任架构变得愈发重要。在数字化时代,云计算技术得到了广泛的应用和推广。企业和组织借助云服务提供商的强大能力,实现了高效、灵活和可扩展的IT基础设施。然而,随着云环境的快速发…...

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

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

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

Docker 中 jdk8容器里无法使用 JDK 的 jmap 等命令的问题
一、问题描述 项目部署在 CentOS 服务器上。项目偶尔会出现无响应的情况,这时理所当然要上去用 JDK 相关命令看看堆栈和GC等信息了。 进入 Java 程序所在容器:docekr-compose exec api bash,进入到 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, 创建类的时候报错 报错信息如下: 原因:新版本的ECLIPSE要求每一个类都必须定义在包里面 解决方法:创建类的时候指定类的名字,如下图:Package 里面填写ch3,表示包名 创建完成…...

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

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

修复中间件log4j漏洞方案(直接更换漏洞jar包)
说明: 后台服务里面的log4j漏洞我们已经全部升级处理了,但是一些中间件镜像包里的log4j漏洞需要单独处理 解决办法以ElasticSearch7.6.2为例: 方法: (1)找到容器里面有哪些旧的log4j依赖包 (…...

怎么压缩pdf文件大小?详细压缩步骤
怎么压缩pdf文件大小?在日常的工作和学习中,我们频繁地处理PDF文件。然而,有时候这些文件的大小可能会非常庞大,这给我们带来了一系列的问题。首先,它们占用了大量的存储空间,使得我们的设备变得拥挤不堪。…...

php 安装rabbitmq:如何使用 PHP 安装 RabbitMQ?
示例示例安装Erlang要在PHP环境中使用,需要先安装Erlang,它是的运行环境。 1、安装Erlang 首先,要在PHP环境中使用RabbitMQ,需要先安装Erlang,它是RabbitMQ的运行环境。 可以使用下面的命令来安装Erlang:…...

算法训练营day44|动态规划 part06:完全背包 (完全背包、 LeetCode518. 零钱兑换 II、377. 组合总和 Ⅳ )
文章目录 完全背包518. 零钱兑换 II (求组合方法数)思路分析代码实现思考总结 377. 组合总和 Ⅳ (求排列方法数)思路分析代码实现思考总结 完全背包 完全背包和01背包问题唯一不同的地方就是,每种物品有无限件。 依然举这个例子: 背包最大重量为4。 物…...

包管理工具--》其他包管理器之cnpm、pnpm、nvm
包管理工具系列文章目录 一、包管理工具--》npm的配置及使用(一) 二、包管理工具--》npm的配置及使用(二) 三、包管理工具--》发布一个自己的npm包 四、包管理工具--》yarn的配置及使用 五、包管理工具--》其他包管理器之cnpm…...

线性代数的学习和整理22:矩阵的点乘(草稿)
4 矩阵乘法 A,B两个同阶同秩N阵,看上去结构一样,但两厢相乘,在做在右,地位差别巨大。 在左,你就是基,是空间的根本,是坐标系,是往哪去、能到哪的定海神针,是如来佛手&a…...

如何在Windows中使用C#填写和提取PDF表单
如何在Windows中使用C#填写和提取PDF表单 PDF表单不仅允许用户填写和提交数据,也允许用户创建各种表单域收集用户的数据,并通过提取表单字段值,将收集和合并提交的数据进一步分析或处理。PDF通过电子方式填写、保存和共享的形式,…...

microsoft.office.interop.word 怎样 读取 某个汉字 字体颜色为红色
SKY[管理]筱傑 SKY[机器]筱淋 microsoft.office.interop.word 怎样 读取 某个汉字 字体颜色为红色呢? 要读取某个汉字的字体颜色是否为红色,您可以使用Microsoft.Office.Interop.Word来进行操作。以下是一个示例代码,可以帮助您实现该功能&am…...

第二十二章 Classes - 调用类方法的快捷方式
文章目录 第二十二章 Classes - 调用类方法的快捷方式调用类方法的快捷方式类参数 第二十二章 Classes - 调用类方法的快捷方式 调用类方法的快捷方式 使用 ObjectScript 调用类方法时,在以下情况下可以省略包(或更高级别的包):…...

标准C++day2——函数重载、默认形参和引用
一、函数重载 1、什么是函数重载? 在同一作用域下,函数名相同,参数列表不同的函数构成重载关系 函数重载与返回值类型、参数名无关 与作用域是否相同,以及参数列表的数量、参数类型、常属性不同等有关 2、C是如何实现函数重载的&a…...

Qt5下遍历QList的方法
lines定义如下 QMap<QString,Line> lines; Line的定义如下 class Line{protected:QString name;QColor color;QList<int> total_stations; // all statuibQList<QString> start_stas,end_stas; //start end stationQList<QList<QString>>sta_li…...