
【Java开发避坑指南】
文章平均质量分 95
本专栏旨在梳理Java开发过程中常见的误区和陷阱,通过实战案例的分析,帮助读者规避风险,提升开发水平。期望本指南能够成为您在Java开发道路上的得力助手,助您一路披荆斩棘,成长为一名优秀的Java工程师
小小工匠
show me the code ,change the world
展开
-
Java避坑案例 - Parallel Stream与ForkJoinPool
Java 8 的 parallel stream 功能,可以让我们很方便地并行处理集合中的元素,其背后是共享同一个 ForkJoinPool,默认并行度是CPU 核数 -1。对于 CPU 绑定的任务来说,使用这样的配置比较合适,但如果集合操作涉及同步 IO 操作的话(比如数据库操作、外部服务调用等),建议自定义一个ForkJoinPool(或普通线程池)原创 2024-11-01 06:15:00 · 4158 阅读 · 0 评论 -
Java避坑案例 - 线程池错误的混用引发的性能故障分析
计算任务打了双引号,是因为我们的模拟代码执行的是休眠操作,并不属于 CPU 绑定的操作,更类似 IO 绑定的操作,如果线程池线程数设置太小会限制吞吐能力。因为开启了CallerRunsPolicy 拒绝处理策略,所以当线程满载队列也满的情况下,任务会在提交任务的线程,或者说调用 execute 方法的线程执行,也就是说。接下来我们模拟一下文件批处理的代码,在程序启动后通过一个线程开启死循环逻辑,不断向线程池提交任务,任务的逻辑是向一个文件中写入大量的数据。写入文件 artisan.txt。原创 2024-10-31 20:45:00 · 5426 阅读 · 0 评论 -
Java避坑案例 - 线程池未复用引发的故障复盘及源码分析
会在需要时创建必要多的线程,业务代码的一次业务操作会向线程池提交多个慢任务,这样执行一次业务操作就会开启多个线程。时不时有报警提示线程数过多,超过2000 个,收到报警后查看监控发现,瞬时线程数比较多但过一会儿又会降下来,线程数抖动很厉害,而应用的访问量变化不大。在项目代码里,我们没有搜到声明线程池的地方,搜索 execute 关键字后定位到,原来是业务代码调用了一个类库来获得线程池,类似如下的业务代码:调用。一般而言,线程池肯定是复用的, 1000 多个线程池肯定不正常啊。原创 2024-10-31 19:45:00 · 3773 阅读 · 0 评论 -
Java避坑案例 - “激进”的线程池扩容策略及实现
重写工作队列的offer方法通过创建一个自定义工作队列,重写offer方法,使其在插入任务时总是返回false,从而模拟队列已满的状态。实现自定义拒绝策略在达到最大线程数后,我们需要定义一个拒绝策略,在这个策略中,再把任务插入到自定义工作队列中。原创 2024-10-30 21:21:19 · 4008 阅读 · 0 评论 -
Java避坑案例 - 线程池使用中的风险识别与应对
以方便排查问题。当出现线程数量暴增、线程死锁、线程占用大量 CPU、线程执行出现异常等问题时,我们往往会抓取线程栈。此时,有意义的线程名称,就可以方便我们定位问题除了建议手动声明线程池以外,还建议。原创 2024-10-30 19:52:58 · 3896 阅读 · 0 评论 -
Java避坑案例 - 异步处理的关键问题与解决方案
异步处理在现代互联网应用中至关重要,但它也伴随着一系列潜在的挑战。可靠性、消息模式区分以及死信队列问题。异步处理虽然好用,但在实现的时候却有三个最容易犯的错,分别是异步处理流程的可靠性问题消息发送模式的区分问题大量死信消息堵塞队列今天我们来详细剖析这些问题并给出一些优化建议。消息可靠性:通过补偿机制和幂等操作保障流程的完整性。消息模式区分:明确广播和工作队列的适用场景,避免误配置导致重复消费。死信队列的使用:确保异常情况得到妥善处理,避免队列阻塞。原创 2024-10-28 21:48:09 · 4031 阅读 · 0 评论 -
Java避坑案例 - 系统上线前如何建立的监控体系
假设有一个是关键外部依赖 ,比如三方服务有一个 user 接口,出现异常的概率是 50%//一半概率返回正确响应,一半概率抛异常else要实现这个user 接口是否正确响应和程序整体的健康状态挂钩的话,很简单,只需定义一个实现接口即可在 health 方法中,我们通过 RestTemplate 来访问这个 user 接口,如果结果正确则返回,并把调用执行耗时和结果作为补充信息加入 Health 对象中。如果调用接口出现异常,则返回,并把异常信息作为补充信息加入 Health 对象中可以通过。原创 2024-10-27 14:45:17 · 4325 阅读 · 0 评论 -
Java避坑案例 - 高并发场景下的分布式缓存策略
通常我们会使用更快的介质(比如内存)作为缓存,来解决较慢介质(比如磁盘)读取数据慢的问题,缓存是用空间换时间,来解决性能问题的一种架构设计模式。更重要的是,磁盘上存储的往往是原始数据,而缓存中保存的可以是面向呈现的数据。这样一来,缓存不仅仅是加快了 IO,还可以减少原始数据的计算工作使用 Redis 或其他缓存系统的确能有效解决系统性能问题,但设计和实现缓存策略时必须仔细考虑潜在问题,否则会适得其反。让我们具体看看这些常见的缓存问题及解决方案。原创 2024-10-26 23:00:05 · 3957 阅读 · 0 评论 -
Java避坑案例 - 接口设计_明确接口的处理方式:同步 vs 异步
使用方可以根据业务性质选择合适的方法:如果是后端批处理使用,那么可以使用同步上传,多等待一些时间问题不大;如果是面向用户的接口,那么接口响应时间不宜过长,可以调用异步上传接口,然后定时轮询上传结果,拿到结果再显示经过改造的同步上传接口syncUpload,用户可以等待上传完成。异步上传接口,快速返回任务 ID,客户端可轮询查询上传结果。这样的设计使得接口行为清晰、可预测,便于用户根据需求选择合适的处理方式。原创 2024-10-24 07:45:00 · 3959 阅读 · 0 评论 -
Java避坑案例 - 接口设计_版本控制策略
在接口设计时,确保系统之间的对话语言统一非常重要,尤其是在接口的命名、参数列表、包装结构体、版本策略、幂等性实现及同步异步处理方式等方面。原创 2024-10-23 22:57:04 · 4373 阅读 · 0 评论 -
Java避坑案例 - 接口设计_明确接口响应
因此,对于第一次调用,收单服务自己没问题,success 是 true,message 是 OK,但调用订单服务时却因为订单风险问题被拒绝,所以 code 是 5001,info 是 Risk order detected,data 中的信息是订单服务返回的,所以最终订单状态是 Cancelled。相比原来混乱的接口定义和处理逻辑,改造后的代码,明确了接口每一个字段的含义,以及对于各种情况服务端的输出和客户端的处理步骤,对齐了客户端和服务端的处理逻辑。的结果,code、info 是调用订单服务的结果。原创 2024-10-23 22:29:16 · 4179 阅读 · 0 评论 -
Java避坑案例 - 消除代码重复_利用属性拷贝工具
转换时大量字段的手动赋值,遇到有上百个属性的复杂类型,非常非常容易出错。我的建议是,不要手动进行赋值,考虑使用Bean 映射工具进行。对于三层架构的系统,考虑到层之间的解耦隔离以及每一层对数据的不同需求,通常每一层都会有自己的。,复制其中大部分的字段,然后把数据入库,势必需要进行很多属性映射赋值操作。这个数据传输对象,描述的是一个订单中的几十个属性。对于复杂的业务系统,实体有几十甚至几百个属性也很正常。这种 Mapping 工具来做 Bean 的转换,如果手动写这些实体之间的赋值代码,同样容易出错。原创 2024-10-23 20:45:00 · 3590 阅读 · 0 评论 -
Java避坑案例 - 消除代码重复_利用注解 + 反射
要实现接口逻辑和逻辑实现的剥离,首先需要以POJO类(只有属性没有任何业务逻辑的数据类)的方式定义所有的接口参数@Data有了接口参数定义,我们就能通过自定义注解为接口和所有参数增加一些元数据。如下所示,我们定义一个接口 API 的注解 BankAPI,包含接口 URL 地址和接口说明@Inherited然后,我们再定义一个自定义注解 @BankAPIField,用于描述接口的每一个字段规范,包含参数的次序、类型和长度三个属性@Inherited接下来,注解就可以发挥威力了。原创 2024-10-22 23:43:21 · 4504 阅读 · 0 评论 -
Java避坑案例 - 消除代码重复_模板方法与工厂模式的最佳实践
如下代码所示,抽象类实现了购物车通用的逻辑,额外定义了两个抽象方法让子类去实现。其中,方法用于计算商品折扣,方法用于计算运费/*** 抽象购物车类,用于处理用户购物车的相关操作*//*** 处理用户购物车,计算总价、运费、折扣等信息* @param userId 用户ID,用于获取用户特定的优惠和配送信息* @param items 购物车中的商品及其数量,键为商品ID,值为数量* @return 返回一个Cart对象,包含处理后的购物车信息*/// 创建一个新的Cart对象。原创 2024-10-22 22:31:10 · 3997 阅读 · 0 评论 -
Java避坑案例 - ConcurrentHashMap 的使用陷阱
JDK 1.5 后推出的,是一个高性能的线程安全的哈希表容器。“线程安全”这四个字特别容易让人误解,因为只能保证提供的原子性读写操作是线程安全的.我们通常误以为使用了后,就不需要担心线程安全问题。然而只能保证单个操作(如putget)的线程安全,无法确保多个操作(如size和putAll)之间的一致性。使用并发工具类的注意事项多操作间状态一致性问题能保证单次操作的线程安全,但无法保证多个操作之间的一致性。避免使用快照方法控制流程在并发情况下,size()等方法只能用作参考,不能用于控制流程逻辑。原创 2024-10-14 20:05:26 · 4323 阅读 · 0 评论 -
Java避坑案例 - 忽略线程重用导致信息错乱
线程池会重用固定的几个线程,一旦线程重用,那么很可能首次从 ThreadLocal 获取的值是之前其他用户的请求遗留的值。行在 Tomcat 中,执行程序的线程是 Tomcat 的工作线程,而 Tomcat 的工作线程是基于。中,来模拟从当前上下文获取到用户信息的逻辑,随后再获取一次值,最后输出两次获得的值和线程名称。按理说,在设置用户信息之前第一次获取的值始终应该是 null,但我们要意识到,程序运。与线程池结合使用时,需要特别小心线程重用的问题。:用户2的请求读取到了用户1的用户ID,说明。原创 2024-10-14 19:30:08 · 4026 阅读 · 0 评论