Java常用并发容器总结(二)
时间:2022-07-24
本文章向大家介绍Java常用并发容器总结(二),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
ConcurrentHashMap
1.介绍
ConcurrentHashMap是一个高效并发的HashMap,它采用了减小锁粒度的手段,内部进一步细分成了若干个小的HashMap,称为Segment段。默认情况下,一个ConcurrentHashMap被分为16个段。多ConcurrentHashMap操作时,并不是将整个ConcurrentHashMap加锁,而是首先根据hashCode定位到要操作的Segment,然后对该段进行加锁。在多线程环境下,如果多个线程操作同一个ConcurrentHashMap的不同Segment,可以做到真正的并行,大大提高了效率。
2.代码分析
下面以ConcurrentHashMap的put()方法为例,分析ConcurrentHashMap的操作策略:
public V put(K key, V value) {
Segment<K,V> s;
if (value == null)
throw new NullPointerException();
//计算key的hashCode
int hash = hash(key);
//根据hashCode,找到要进行操作的段
int j = (hash >>> segmentShift) & segmentMask;
if ((s = (Segment<K,V>)UNSAFE.getObject
(segments, (j << SSHIFT) + SBASE)) == null)
s = ensureSegment(j);
//对该段进行put
return s.put(key, hash, value, false);
}
可以看出,对ConcurrentHashMap的put操作,可以将粒度减小为对某一个Segment的操作,大大减小了锁的竞争,提高并发效率。 但是,减小锁的粒度引入了一个新的问题,当系统需要获得全局锁时,消耗的资源较多。以size()方法为例:
public int size() {
final Segment<K,V>[] segments = this.segments;
int size;
boolean overflow;
long sum;
long last = 0L;
int retries = -1;
try {
for (;;) {
if (retries++ == RETRIES_BEFORE_LOCK) {
for (int j = 0; j < segments.length; ++j)
//在这里,需要多每一个Segment分别加锁
ensureSegment(j).lock();
}
sum = 0L;
size = 0;
overflow = false;
for (int j = 0; j < segments.length; ++j) {
Segment<K,V> seg = segmentAt(segments, j);
if (seg != null) {
//计算size总数
sum += seg.modCount;
int c = seg.count;
if (c < 0 || (size += c) < 0)
overflow = true;
}
}
if (sum == last)
break;
last = sum;
}
} finally {
if (retries > RETRIES_BEFORE_LOCK) {
for (int j = 0; j < segments.length; ++j)
//这里再分别释放每一段的锁
segmentAt(segments, j).unlock();
}
}
return overflow ? Integer.MAX_VALUE : size;
}
可以看出,在高并发的场景下,ConcurrentHashMap的size()方法的效率要明显低于HashMap。
3.适用场景
简单地说,如果想在高并发的场景下使用HashMap,那么ConcurrentHashMap将是一个很好的选择。
- gradle中使用嵌入式(embedded) tomcat, debug 启动
- spring in action 4th --- quick start
- Date, TimeZone, MongoDB, java中date的时区问题
- spring boot 添加拦截器
- spring boot 部署为jar
- 重定向Http status code 303 和 302
- centos7查看系统版本,查看机器位数x86-64
- 在centos7中添加一个新用户,并授权
- 如何优化coding
- 在PowerShell中使用curl(Invoke-WebRequest)
- linux centos中添加删除修改环境变量,设置java环境变量
- CentOS7下安装mysql5.6修改字符集为utf8并开放端口允许远程访问
- CentOS7下mysql5.6修改默认编码
- 在idea中maven项目jdk编译version总是跳到1.5
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- PHP常用函数之根据生日计算年龄功能示例
- Laravel 之url参数,获取路由参数的例子
- PHP call_user_func和call_user_func_array函数的简单理解与应用分析
- PHP常用函数之获取汉字首字母功能示例
- 浅谈laravel-admin form中的数据,在提交后,保存前,获取并进行编辑
- tp5 实现列表数据根据状态排序
- mac pecl 安装php7.1扩展教程
- tp5.1 实现setInc字段自动加1
- Laravel 前端资源配置教程
- laravel框架语言包拓展实现方法分析
- PHP使用JpGraph绘制折线图操作示例【附源码下载】
- Laravel Eloquent分表方法并使用模型关联的实现
- 关于laravel模板中生成URL的几种模式总结
- Laravel基础-关于引入公共文件的两种方式
- Laravel框架Blade模板简介及模板继承用法分析