一: 日期YYYY格式设置的坑
解析:设置的2019-12-31,可结果显示的2020-12-31,因为YYYY是基于周来计算年的,它指向当天所在周属于的年份,一周从周日开始算起,周六结束,只要本周跨年,那么这一周就算下一年的了。
正确姿势:使用yyyy格式。(实际使用时尽可能是用yyyy)
二:金额计算精度丢失
解析:发现以上结果还是不对,使用 BigDecimal 表示和计算浮点数,必须使用「字符串的构造方法」来初始化 BigDecimal
正确姿势:(实际操作中使用BigDecimal注意加引号)
三:FileReader默认编码导致乱码问题
解析:系统默认编码是GBK,文件内容的编码是UTF-8,demo中读取出来,出现乱码了,FileReader 是以当「前机器的默认字符集」来读取文件的,如果希望指定字符集的话,需要直接使用 InputStreamReader 和 FileInputStream。
正确姿势:使用InputStreamReader指定要读文件的编码格式(实际使用时在读包含中文的文件时注意文件的编码格式,保持一致)
四:Integer缓存的坑
解析:编译器会把 Integer a = 127 转换为 Integer.valueOf(127),通过翻源码我们发现:
i在一定范围内,是会返回缓存的。默认情况这个缓存的区间大小是[-128, 127],还有呢,设置 JVM 参数加上 -XX:AutoBoxCacheMax=1000,是可以调整这个区间参数的。
正确姿势:(包装类使用equals比较内容,==用于比较地址,及时是Integer会自动装箱、拆箱)
五:static静态变量依赖spring实例化变量,可能导致初始化出错
解析:这个静态的smsService有可能获取不到的,因为类加载顺序不是确定的
正确姿势:(现在用的少了)
六:使用ThreadLocal,线程重用导致信息错乱
解析:按理说每次获取的before应该都是null,但是呢,程序运行在 Tomcat 中,执行程序的线程是 Tomcat 的工作线程,而 Tomcat 的工作线程是基于线程池的,线程池会重用固定的几个线程,一旦线程重用,那么很可能首次从 ThreadLocal 获取的值是之前其他用户的请求遗留的值。这时,ThreadLocal 中的用户信息就是其他用户的信息。
用户1,请求过来,会有以下结果,【符合预期】:
用户2请求过来,会有以下结果,【不符合预期】:
正确姿势:(在finally代码块中删除ThreadLocal中的数据,确保数据不串)
七: Arrays.asList的几个坑
解析:基本类型不能作为 Arrays.asList方法的参数,否则会被当做一个参数。
Arrays.asList源码:
7.2:Arrays.asList 返回的 List 不支持增删操作
解析:Arrays.asList 返回的 List 并不是我们期望的 java.util.ArrayList,而是 Arrays 的内部类 ArrayList。内部类的ArrayList没有实现add方法,而用的是父类的add方法,参数不对,是会抛出异常。
7.3:使用Arrays.asList的时候,对原始数组的修改会影响到我们获得的那个List
正确姿势:实际如果需要可以用new ArrayList(Arrays.asList(arr))包一下( java.util包下的)。
八:生产环境异常打印常见的坑
解析:只保留了异常消息,栈没有记录啦
解析:因为它占用太多内存,造成锁死,并且,日志交错混合,也不易读
正确姿势:
8.2:线程池里的 throw new RuntimeException会被吞掉
运行结果:
正确姿势:任务代码使用try/catch捕获异常
九:使用Executors声明线程池,newFixedThreadPool的OOM问题
解析:看下源码,其实newFixedThreadPool使用的是无界队列!
newFixedThreadPool线程池的核心线程数是固定的,它使用了近乎于无界的LinkedBlockingQueue阻塞队列。当核心线程用完后,任务会入队到阻塞队列,如果任务执行的时间比较长,没有释放,会导致越来越多的任务堆积到阻塞队列,最后导致机器的内存使用不停的飙升,造成JVM OOM。
正确姿势:使用 Semaphore(信号量)控制进入阻塞队列的数据,在线程池里的任务每执行完一个任务释放一个信号量,此外需要注意:
为了避免出现问题,在主线程中可以使用semaphore.availablePermits()+while true来判断子线程是否结束,尤其是定时任务+数据量大+定时任务执行时间周期短,防止在本次子线程还未结束,下一次主线程进来又一次执行子线程的逻辑,造成子线程的逻辑重复执行,数据库出现重复数据。
十:先查询,再更新/删除的并发一致性问题
正确姿势:
一种情况:
二种情况:使用事务
十一:事务未生效的坑
1、在非public修饰的方法使用
2、本类方法直接调用
3、异常被try...catch吃了,导致事务失效
十二:Mysql引号用错位置,线上数据某字段全部变成0
原表数据:
执行以下sql:
UPDATE student set name="bbbb where name"="bb" name的字段值全部变成了0
解析:这种sql虽然说单条执行错误很容易被看出来,但是如果你是批量执行的时候就很容易出错,或者是在线上执行的时候,语法不错全表更新,心态崩了!!!!
由于SELECT "bbbb where name"="bb"的结果是0,sql服务器将
UPDATE student set name="bbbb where name"="bb" 解析成
UPDATE student set name=("bbbb where name"="bb") 进而解析成
UPDATE student set name-0 执行全表扫描