One Trick Per Day

简介: 本文分享Java开发中的6个实用技巧:合理初始化Map容量、避免使用Executors创建线程池、Arrays.asList不可变性、高效遍历Map、SimpleDateFormat线程安全及并发记录加锁策略,提升系统性能与稳定性。(238字)

One Trick Per Day

1.初始化Map大小并非用多少指定多少
初始化Map并非用多少初始化Size是多少,建议使用Guava,避免扩容引起的动荡()
说明
如:Map<String, String> map = new HashMap<>(1); 在具体使用时,并非size=1,而是最近的2的幂等,如1实际是2,3实际是4,9实际是16
使用方法
依赖gvaua:Map<String, String> map = Maps.newHashMapWithExpectedSize(7);
手动声明:Map<String, String> 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之后不要调用修改操作

Java

运行代码复制代码

1

2

String[] str = new String[] { "you", "wu" };

List list = Arrays.asList(str);

因为asList返回的实际是一个Arrays内部类,并没有实现集合的修改方法(add/remove/clear)// 当操作修改方法时,会报UnsupportedOperationException。
第一种情况:list.add("yangguanbao"); 运行时异常。
第二种情况:str[0] = "gujin"; 那么 list.get(0)也会随之修改。[涉及栈堆指针操作,修改数组的数据,导致同样引用该数据的list值被改变]

stack

heap

wu

list

str[o]"haha";

youhaha

str


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<DateFormat> df = new ThreadLocal<DateFormat>() {

@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 次

相关文章
|
2月前
|
人工智能 自然语言处理 API
全面认识MCP:大模型连接真实世界的“USB-C接口”
MCP(模型上下文协议)是Anthropic推出的开放标准,被誉为AI时代的“USB-C接口”,旨在统一大模型与外部工具、数据源的连接方式。它通过标准化通信,让AI智能体能高效调用天气、数据库等各类工具,打破“工具孤岛”,简化开发流程,推动AI应用从对话走向真实世界任务执行,加速构建安全、可扩展的智能生态。
|
2月前
|
Java 开发工具 数据安全/隐私保护
项目《中州养老》
项目《中州养老》是一个面向养老院的后台管理系统,涵盖员工管理端与家属小程序端,实现预约、入住、健康监测等全流程管理。系统采用SpringBoot+Vue3技术栈,集成Redis缓存、阿里云IoT平台与OSS存储,支持智能设备数据采集与实时报警,提升养老管理智能化水平。
 项目《中州养老》
|
2月前
|
消息中间件 监控 Java
RocketMQ:底层Netty频繁OS OOM
本文分析了一起RocketMQ应用因Netty频繁申请堆外内存导致OS OOM的问题。根本原因是多个ClassLoader加载了多个PooledByteBufAllocator实例,各自独立占用堆外内存,突破JVM的MaxDirectMemorySize限制。结合Arthas、NMT等工具深入排查,最终定位到rocketmq-client实例占用近1G堆外内存。建议短期调小Java堆以腾出空间,长期优化Netty内存使用与类加载机制。
 RocketMQ:底层Netty频繁OS OOM
|
2月前
|
Java Linux Apache
Docker
本文介绍Docker基础知识与实战操作,涵盖镜像打包、容器运行、日志查看及Dockerfile编写等内容,帮助开发者快速掌握Docker核心技能并实现Java项目容器化部署。
Docker
|
2月前
|
Java Linux 开发工具
Linux
本文介绍如何在Linux系统上部署SpringBoot应用。内容涵盖项目打包、JAR文件上传、JDK安装与配置、应用启动及健康检查接口验证,助力快速完成Java应用的Linux环境部署。
 Linux
|
2月前
|
负载均衡 Dubbo 应用服务中间件
Day01
Day01:微服务并非绝对优于单体,需结合业务场景。简单应用单体更优,复杂系统适合微服务。常用中间件如Nacos、OpenFeign、Gateway等。Nacos支持心跳机制与主动检测,临时实例异常剔除,非临时则保留。对比Eureka,Nacos在服务发现、健康检测、集群模式上更具优势。负载均衡常用轮询算法。
|
2月前
|
存储 负载均衡 Java
第九章 SpringCloud框架
本章介绍SpringCloud核心组件:Nacos服务注册与分级存储模型、Eureka对比;OpenFeign调用流程及负载均衡策略;Sentinel限流熔断原理与滑动窗口算法;Gateway路由断言、过滤器实现机制,全面解析微服务架构关键技术。
|
2月前
|
存储 SQL 关系型数据库
第四章 数据库
本章系统讲解MySQL数据库核心知识,涵盖char与varchar区别、事务ACID特性及隔离级别、索引结构(B+树)、聚簇索引与二级索引、回表查询、索引创建与失效场景,并结合explain执行计划分析慢SQL及优化策略。
 第四章 数据库
|
2月前
|
NoSQL 前端开发 Java
3-代码拉取与运行
本文介绍JeecgBoot前后端项目部署流程,包括代码拉取(GitHub/离线)、数据库脚本导入、Idea工程配置、Redis与数据库连接修改、后端启动及Swagger接口访问,前端Vue3项目在VSCode中启动并配置代理,实现前后端联调运行。
 3-代码拉取与运行
|
2月前
|
消息中间件 人工智能 NoSQL
RocketMQ:A2A协议实现多智能体优化
Apache RocketMQ推出LiteTopic轻量级通信模型,结合A2A协议与AgentScope框架,为多智能体系统提供高可靠、低延迟的异步通信方案,支持会话持久化、断点续传与动态协同,助力AI应用构建稳定高效的协作基座。
 RocketMQ:A2A协议实现多智能体优化