One Trick Per Day

简介: 初始化Map应避免直接指定大小,建议用Guava或手动计算容量;禁用Executors创建线程池,防止OOM,推荐自定义ThreadPoolExecutor;Arrays.asList返回不可变列表,禁止修改操作;遍历Map优先使用entrySet或forEach;SimpleDateFormat非线程安全,勿定义为static,推荐ThreadLocal或Java8新时间类;并发修改记录需加锁,优先乐观锁,冲突高则用悲观锁。

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 次

相关文章
|
15小时前
|
敏捷开发 Dubbo Java
需求开发人日评估
随着敏捷开发普及,工时评估成关键环节。本文介绍基于人日的开发周期评估方法,涵盖开发、自测、联调、测试及发布各阶段,并提供常见需求如增删改查、导入导出、远程调用等的参考人日,助力团队科学规划迭代。
|
14小时前
|
负载均衡 算法 搜索推荐
重复性工作中如何培养匠心
本文探讨在互联网低增长周期下,如何在重复性工作中培养“匠心”。通过分析结果价值、过程贡献与可预测性三个维度,指出即便在熟稔的领域,也能通过深挖价值、主动创新、超越预期来拓展成长空间,实现从执行到卓越的跃迁。
|
14小时前
|
架构师 前端开发 中间件
技术新人成长到Leader的路径
本文为技术人成长进阶指南,涵盖新人到技术leader的四大阶段:从踏实做事、建立思维框架,到激发自我驱动力、打造核心竞争力;从修炼全局视角、成事思维,到成就他人、创造价值感。强调以事修人、以人成事,助力技术人实现持续成长与突破。(238字)
|
15小时前
|
测试技术
发布模式
蓝绿部署是一种快速、低风险的应用发布策略,通过维护两套独立系统(运行中的“绿色”与待发布的“蓝色”),实现无缝切换与快速回滚,减少发布中断。测试稳定后流量切至蓝色系统,原绿色系统可下线。适用于内聚性强、切换简单的系统。不同于金丝雀发布(渐进式替换)和A/B测试(效果对比),蓝绿部署聚焦于平稳上线,保障稳定性。
|
15小时前
|
机器人 Java API
钉钉通知
创建钉钉机器人并设置告警群,通过Webhook获取API地址。使用PostMan测试文本、@指定人及卡片消息发送。编写Java代码调用官方SDK实现消息推送,封装工具类并与Nacos配置中心集成,实现异常日志等场景下的实时告警通知,确保问题及时处理。
|
15小时前
|
小程序 Java
微信通知
基于企业微信与小程序集成,通过appId和secret获取access_token,实现消息发送。需配置企业微信及绑定小程序,使用Java异步调用接口,示例包含用户信息实体类定义,支持userid、name等字段设置,便于消息推送与用户管理。(238字)
|
15小时前
|
缓存 NoSQL Redis
Day08 每日必会
深入解析Redis核心机制:涵盖缓存三剑客(穿透、击穿、雪崩)成因与应对;持久化、过期与淘汰策略原理;哨兵选举流程;主从同步机制(全量与增量);以及“单线程”模型的真相,全面掌握Redis高性能背后的理论基础。
|
15小时前
Day07 每日必会
CAP理论指出分布式系统中一致性、可用性、分区容错性三者只能取其二,因网络不可靠,P必然存在,故需在A和C间权衡。BASE理论提出基本可用、软状态、最终一致性的思路,平衡CAP矛盾。Seata AT模式通过全局事务ID协调分支事务,实现分布式事务控制。MQ防消息丢失、重复消费及积压,需结合持久化、确认机制、幂等处理与批量消费等策略。
|
15小时前
|
存储 缓存 NoSQL
Day06 每日必会
Redis常见数据结构包括:字符串、哈希、列表、集合、有序集合及地理空间索引。持久化机制有AOF和RDB,配合使用可有效防数据丢失。三大缓存问题:雪崩、穿透、击穿,需通过过期时间随机化、布隆过滤器、分布式锁等手段应对。
|
15小时前
|
消息中间件 SQL 缓存
Day05 每日必会
为确保消息不丢失,RabbitMQ提供三大机制:生产者确认(通过唯一ID与ACK/NACK反馈)、消息持久化(队列、消息、交换机持久化)和消费者确认(手动/自动ACK)。我们采用auto模式+重试处理。延迟消息可通过TTL或DelayExchange插件实现,常用于超时订单场景。消息积压可扩消费者或队列容量解决。MyBatis中#{}预编译防注入,${}直接拼接;有一级会话缓存和二级Mapper缓存;SQL执行经SqlSession解析MappedStatement,交由Executor执行。

热门文章

最新文章