缺陷 & 规避

  1. NPE 空指针异常(Null Pointer Exception)

1)使用注解 @NotNull 和 @Nullable

2)用 Optional 处理链式调用

3)用 Objects.equals(a,b) 代替 a.equals(b),能够避免任意对象为 null 时的 NPE。

4)使用空对象模式,空对像模式通过一个特殊对象代替不存在的情况,代表对象不存在时的默认行为模式。例如:用 Empty List 代替 null,EmptyList 能够正常遍历:Collections.emptyList();

  1. 线程安全

JVM 的内存模型十分复杂,难以理解, <<Java 并发编程实战>>告诉我们,除非你对 JVM 的线程安全原理十分熟悉,否则应该严格遵守基本的 Java 线程安全规则,使用 Java 内置的线程安全的类及关键字。

熟练使用线程安全类 ConcurrentHashMap

java 复制代码
public class ConcurrentHashMapExample {
    private Map<String, String> map = new ConcurrentHashMap<>();

    public void append(String key, String suffix) {
        // 使用 computeIfPresent 原子操作
        map.computeIfPresent(key, (k, v) -> v + suffix);
    }
}

保证变更的原子性

使用不可变对象

**正确性优先于性能:**不要因为担心性能问题而放弃使用 synchronized,volatile 等关键字,或者采用一些非常规写法。

多线程环境中的volatile:在多线程编程中,每个线程可以拥有变量的独立拷贝,这可能导致数据不一致的问题。声明一个变量为volatile可以保证所有线程看到的都是主存中的最新版本,从而在一定程度上解决了线程间可见性的问题。尽管volatile不保证复合操作的原子性,它通过内存屏障来防止指令重排序,确保了操作的顺序性。

Java中的volatile:在Java中,volatile不仅保证了单个变量的可见性,还可以用于实现轻量级的同步,特别是在使用双重检查锁定模式(Double-Checked Locking)设计单例模式时。volatile关键字通过内存屏障来防止指令重排,这对于维持对象引用的可见性和一致性至关重要。

线程池使用不当

java 复制代码
public class ThreadPoolExample {

    // 没有任何限制的线程池, 使用起来很方便, 但当一波请求高峰到达时, 可能会创建大量线程, 导致系统崩溃
    private static Executor executor = Executors.newCachedThreadPool();

}

手动创建线程池

java 复制代码
public class ManualCreateThreadPool {

    // 手动创建资源有限的线程池
    private Executor executor = new ThreadPoolExecutor(10, 10, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<>(1000),
        new ThreadFactoryBuilder().setNameFormat("work-%d").build());
}

通过 AOP 统一异常处理

  1. 避免未知异常抛给调用方, 将未知异常转为 Result 或者通用异常类型

  2. 统一异常日志的打印和监控

处理 Checked Exception

Checked Exception 是在编译期要求必须处理的异常,也就是非 RuntimeException 类型的异常,但 Java Checked 的异常给接口的调用者造成了一定的负担,导致异常声明层层传递,如果顶层能够处理该异常,我们可以通过 lombok 的 @SneakyThrows 注解规避 Checked exception。

Try catch 线程逻辑

特殊异常的处理

InterruptedException 一般是上层调度者主动发起的中断信号,例如某个任务执行超时,那么调度者通过将线程置为 interuppted 来中断任务,对于这类异常我们不应该在 catch 之后忽略,应该向上抛出或者将当前线程置为 interuppted。

避免 catch Error

不要吞并 Error,Error 设计本身就是区别于异常,一般不应该被 catch,更不能被吞掉。举个例子,OOM 有可能发生在任意代码位置,如果吞并 Error,让程序继续运行,那么以下代码的 start 和 end 就无法保证一致性。

内存/资源泄漏

虽然 JVM 有垃圾回收机制,但并不意味着内存泄漏问题不存在,一般内存泄漏发生在在长时间持对象无法释放的场景,比如静态集合,内存中的缓存数据,运行时类生成技术等。

Spring 事务问题,注意事务注解失效的场景

当打上 @Transactional 注解的 spring bean 被注入时,spring 会用事务代理过的对象代替原对象注入。

但是如果注解方法被同一个对象中的另一个方法里面调用,则该调用无法被 Spring 干预,自然事务注解也就失效了。

java 复制代码
 @Component
 public class TransactionNotWork {
 ​
     public void doTheThing() {
         actuallyDoTheThing();
     }
 ​
     @Transactional
     public void actuallyDoTheThing() {
     }
 }
相关推荐
Boilermaker19921 小时前
[Java 并发编程] Synchronized 锁升级
java·开发语言
saoys1 小时前
Opencv 学习笔记:图像掩膜操作(精准提取指定区域像素)
笔记·opencv·学习
Cherry的跨界思维1 小时前
28、AI测试环境搭建与全栈工具实战:从本地到云平台的完整指南
java·人工智能·vue3·ai测试·ai全栈·测试全栈·ai测试全栈
MM_MS1 小时前
Halcon变量控制类型、数据类型转换、字符串格式化、元组操作
开发语言·人工智能·深度学习·算法·目标检测·计算机视觉·视觉检测
꧁Q༒ོγ꧂2 小时前
LaTeX 语法入门指南
开发语言·latex
njsgcs2 小时前
ue python二次开发启动教程+ 导入fbx到指定文件夹
开发语言·python·unreal engine·ue
alonewolf_992 小时前
JDK17新特性全面解析:从语法革新到模块化革命
java·开发语言·jvm·jdk
一嘴一个橘子2 小时前
spring-aop 的 基础使用(啥是增强类、切点、切面)- 2
java
sheji34162 小时前
【开题答辩全过程】以 中医药文化科普系统为例,包含答辩的问题和答案
java
古城小栈2 小时前
Rust 迭代器产出的引用层数——分水岭
开发语言·rust