One Trick Per Day

简介: 初始化Map宜用Guava指定预期大小,避免扩容;禁用Executors创建线程池,防止OOM,推荐自定义或使用Guava;Arrays.asList返回不可变列表,禁止修改操作;遍历Map优先使用entrySet或forEach;SimpleDateFormat非线程安全,建议用ThreadLocal或Java8新时间类;并发更新记录需加锁,推荐乐观锁配合version字段。

1.初始化Map大小并非用多少指定多少
初始化Map并非用多少初始化Size是多少,建议使用Guava,避免扩容引起的动荡()
说明
如:Map map = new HashMap<>(1); 在具体使用时,并非size=1,而是最近的2的幂等,如1实际是2,3实际是4,9实际是16
使用方法
依赖gvaua:Map map = Maps.newHashMapWithExpectedSize(7);
手动声明:Map map = new HashMap<>(实际存储个数 / 0.75 + 1);
2.线程池初始化严禁使用Executors
使用线程池时候,我们可能会使用下面四个场景,这在alibaba代码规范中都是明令禁止的
我们先来一个简单的例子,模拟一下使用 Executors 导致 OOM 的情况。
通过指定 JVM 参数:-Xmx8m -Xms8m 运行以上代码,会抛出 OOM:
以上代码指出,ExecutorsDemo.java 的第 16 行,就是代码中的 executor.execute(new SubThread());。
通过上面的例子,我们知道了 Executors 创建的线程池存在 OOM 的风险,那么到底是什么原因导致的呢?我们需要深入 Executors 的源码来分析一下。其实,在上面的报错信息中,我们是可以看出蛛丝马迹的,在以上的代码中其实已经说了,真正的导致 OOM 的其实是 LinkedBlockingQueue.offer 方法。
如果读者翻看代码的话,也可以发现,其实底层确实是通过 LinkedBlockingQueue 实现的:
如果读者对 Java 中的阻塞队列有所了解的话,看到这里或许就能够明白原因了。Java 中 的 BlockingQueue 主 要 有 两 种 实 现, 分 别 是 ArrayBlockingQueue 和 LinkedBlockingQueue。ArrayBlockingQueue 是一个用数组实现的有界阻塞队列,必须设置容量。LinkedBlockingQueue 是一个用链表实现的有界阻塞队列,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为 Integer.MAX_VALUE。这里的问题就出在:不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE。也就是说,如果我们不设置 LinkedBlockingQueue 的容量的话,其默认容量将会是 Integer.MAX_VALUE。 而 newFixedThreadPool 中创建 LinkedBlockingQueue 时,并未指定容量。此时,LinkedBlockingQueue 就是一个无边界队列,对于一个无边界队列来说,是可以不断的向队列中加入任务的,这种情况下就有可能因为任务过多而导致内存溢出问题。上面提到的问题主要体现在 newFixedThreadPool 和 newSingleThreadExecutor 两个工厂方法上,并不是说newCachedThreadPool 和 newScheduledThreadPool 这两个方法就安全了,这两种方式创建的最大线程数可能是Integer.MAX_VALUE,而创建这么多线程,必然就有可能导致 OOM
正确使用:
这种情况下,一旦提交的线程数超过当前可用线程数时,就会抛出java.util.concurrent.RejectedExecutionException,这是因为当前线程池使用的队列是有边界队列,队列已经满了便无法继续处理新的请求。但是异常(Exception)总比发生错误(Error)要好。
但是部分alibaba作者更推荐使用guava创建对应的线程池,示例如下:
通过上述方式创建线程时,不仅可以避免 OOM 的问题,还可以自定义线程名称,更加方便的出错的时候溯源。
3.Arrays.asList之后不要调用修改操作
因为asList返回的实际是一个Arrays内部类,并没有实现集合的修改方法(add/remove/clear)// 当操作修改方法时,会报UnsupportedOperationException。
第一种情况:list.add("yangguanbao"); 运行时异常。
第二种情况:str[0] = "gujin"; 那么 list.get(0)也会随之修改。[涉及栈堆指针操作,修改数组的数据,导致同样引用该数据的list值被改变]
4.使用 entrySet 遍历 Map 类集合 KV
说明:keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出key 所对应的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效率更高。
如果是 JDK8,使用 Map.foreach 方法。
正例:values()返回的是 V 值集合,是一个 list 集合对象;keySet()返回的是 K 值集合,是一个 Set 集合对象;entrySet()返回的是 K-V 值组合集合。
5.SimpleDateFormat不要定义为static
SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果定义为static,必须加锁,或者使用 DateUtils 工具类。
正例:注意线程安全,使用 DateUtils。亦推荐如下处理:
Java
运行代码
复制代码
1
2
3
4
5
6
private static final ThreadLocal df = new ThreadLocal() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
说明:如果是 JDK8 的应用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar,DateTimeFormatter 代替 SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。
6.并发修改同一记录时需要加锁
要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用 version 作为更新依据。
说明:如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于 3 次

相关文章
|
18小时前
|
负载均衡 算法 网络协议
负载均衡:节点负载差距这么大,为什么收到的流量还一样?
本文深入讲解RPC框架中的负载均衡机制,对比传统Web负载均衡的局限,阐述RPC如何通过客户端自主选节点实现高效、灵活的流量分发。针对业务提出的“智能调流”需求,提出自适应负载均衡方案:通过采集服务节点的CPU、内存、响应耗时等指标,动态打分并调整权重,结合随机加权算法实现流量智能分配,有效避免高负载或低性能节点拖累整体服务可用性,提升系统弹性与稳定性。
|
19小时前
|
运维 监控 负载均衡
健康检测:这个节点都挂了,为啥还要疯狂发请求?
本文深入探讨RPC框架中的服务健康检测机制,针对超大规模集群中节点“半死不活”导致请求失败的问题,分析传统心跳机制的局限性。提出结合业务请求可用率的动态评估方案,避免误判与漏判,并通过多机房部署探活程序提升检测可靠性,最终实现“推拉结合,以拉为准”的最终一致性目标,保障业务高可用。
|
19小时前
|
存储 缓存 负载均衡
服务发现:到底是要 CP 还是 AP?
本文深入探讨RPC框架中服务发现机制,解析其在超大规模集群下的挑战。传统DNS和VIP方案因缓存延迟、扩展性差等问题难以满足需求;ZooKeeper虽能实现强一致服务发现,但在海量节点并发注册时易引发性能瓶颈。为此,提出基于消息总线的最终一致性注册中心方案,通过异步推拉结合、增量更新与两级缓存,保障系统高可用与高效服务下发,更适配大规模分布式场景。
|
19小时前
|
网络协议 算法 前端开发
架构设计:设计一个灵活的 RPC 框架
本文回顾了RPC通信原理,详解其核心架构设计:通过传输、协议、引导与服务发现等模块实现远程调用。同时引入插件化架构,提升框架可扩展性与维护性,助力构建灵活、健壮的RPC系统。(238字)
|
20小时前
|
机器学习/深度学习 搜索推荐 算法
推荐引擎:没有搜索词,「头条」怎么找到你感兴趣的文章?
本文深入解析资讯类App推荐引擎的检索技术,揭秘“下拉刷新”背后如何实现个性化内容推荐。重点讲解基于内容召回与协同过滤(用户/物品)两大核心算法,剖析其原理、优缺点及实际应用,并介绍多路召回与分层排序的混合推荐机制,展现推荐系统如何高效实现精准内容分发。
|
20小时前
|
机器学习/深度学习 搜索推荐 算法
广告系统:广告引擎如何做到在 0.1s 内返回广告信息?
广告系统是互联网核心营收支柱,支撑Google、Facebook等公司超80%收入。本文详解其高性能引擎架构:通过标签检索、向量匹配、非精准打分预筛与索引精简四大优化,在高并发下实现0.1秒内精准投放,揭示“千人千面”背后的工程智慧。
|
20小时前
|
存储 缓存 NoSQL
存储系统:从检索技术角度剖析 LevelDB 的架构设计思想
LevelDB是Google开源的高性能键值存储系统,基于LSM树优化,采用跳表、读写分离、SSTable分层与Compaction等技术,结合BloomFilter、索引分离及LRU缓存,显著提升读写与检索效率,广泛应用于工业级系统。
|
20小时前
|
存储 机器学习/深度学习 算法
最近邻检索(下):如何用乘积量化实现「拍照识花」功能?
AI时代,图片检索依赖高效向量相似搜索。本文解析聚类算法与局部敏感哈希差异,详解K-Means、乘积量化与倒排索引结合技术,揭示以图搜图背后原理,助你理解高维空间近似最近邻的优化策略。(239字)
|
20小时前
|
搜索推荐 UED 索引
最近邻检索(上):如何用局部敏感哈希快速过滤相似文章?
在搜索引擎与推荐系统中,相似文章去重至关重要。通过向量空间模型将文档转化为高维向量,利用SimHash等局部敏感哈希技术生成紧凑指纹,结合海明距离与抽屉原理分段索引,可高效实现近似最近邻检索,在海量数据中快速识别内容相近文章,提升结果多样性与用户体验。
|
20小时前
|
存储 搜索推荐 定位技术
空间检索(下):「查找最近的加油站」和「查找附近的人」有何不同?
本文探讨了在动态查询范围内高效检索“最近的K个目标”的技术方案。针对GeoHash固定范围查询的局限,提出利用四叉树与非满四叉树实现空间动态划分,通过父子节点层级关系快速扩大检索范围,避免重复查找。同时,结合前缀树优化GeoHash字符串编码的索引效率,提升查询性能。进一步扩展至高维场景,介绍k-d树等结构的应用与挑战,为地理位置服务、推荐系统等提供底层支持。(238字)