1 | package me.tianshuang; |
以上的双重检查实现并不安全,原因为编译器可以重排序代码以使 Helper()
构造函数中的代码在写入 helper
变量后执行。如果发生了这样的情况,那么在构造线程写入 helper
变量之后,但在它实际完成对象构造之前,另一个线程可以在完成初始化之前出现并读取 helper
,此时,该线程可能会看到对 helper
对象的非空引用,但会看到 helper
对象字段的默认值,而不是构造函数中设置的值。
1 | package me.tianshuang; |
以上的双重检查实现并不安全,原因为编译器可以重排序代码以使 Helper()
构造函数中的代码在写入 helper
变量后执行。如果发生了这样的情况,那么在构造线程写入 helper
变量之后,但在它实际完成对象构造之前,另一个线程可以在完成初始化之前出现并读取 helper
,此时,该线程可能会看到对 helper
对象的非空引用,但会看到 helper
对象字段的默认值,而不是构造函数中设置的值。
最近看 Joshua Bloch 的访谈 More Effective Java With Google’s Joshua Bloch 时,其中提到单元测试不足以确保代码能够正常工作,举了二分搜索的例子,二分搜索的文章详见:Google AI Blog: Extra, Extra - Read All About It: Nearly All Binary Searches and Mergesorts are Broken,本文在此作简要记录。
在 JDK 1.3.1_28 中 java.util.Arrays#binarySearch(int[], int)
的实现如下:
1 | /** |
之前查看 MyBatis 的代码时,发现默认使用的执行器类型为 SIMPLE,其源码位于 SimpleExecutor.java,而还有一种执行器类型为 REUSE,其源码位于 ReuseExecutor.java,如果比较源码可以发现主要的不同为 ReuseExecutor
含有如下的 HashMap
用于缓存之前创建的 PreparedStatement
:
1 | private final Map<String, Statement> statementMap = new HashMap<>(); |
关于 SQL 注入的防范,个人认为最可靠的方式还是使用 PreparedStatement
,对于替换表名、列名等元数据字符串的场景,使用白名单对填入的值进行严格控制再传入。
在 MyBatis 的 官方文档 中,就对 ${}
的使用进行了如下提示:
It’s not safe to accept input from a user and supply it to a statement unmodified in this way. This leads to potential SQL Injection attacks and therefore you should either disallow user input in these fields, or always perform your own escapes and checks.
PreparedStatement
如何避免出现SQL注入漏洞
SQL Injection Prevention - OWASP Cheat Sheet Series
mybatis – MyBatis 3 | Mapper XML Files
在之前处理线上事故的经验中,我发现不少问题是由于开发未设置远程调用的超时时间或者设置的超时时间较长在服务不稳定时导致线程堆积,最终导致有界线程池被打满,触发拒绝策略,影响线上服务可用性,解决方案还是尽可能的设置可接受的超时时间,而不是一味的使用默认配置或者很大的超时时间。部分开发为了自己负责的业务可用,将全局的超时时间调大,就为后续线上服务的稳定性埋下了隐患,原本在低超时时间设定下可以快速失败的请求,会长时间占用线程直到超时,导致线程堆积,服务不可用。
我在系统设计上犯过的14个错
Threads stuck in java.net.SocketInputStream.socketRead0 – Fast thread