线上datadog
最近总是在报警网站在登录时颁发证书操作耗时太长,即spring security oauth2 endpoint /oauth/token
这个API performance慢.
在阅读源码的时候看到了个有趣的地方,如下:
1 | # org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory |
这段代码只要目的是对JWT token
进行加密,并且这里使用的是非对称加密
.
其中这段代码的上半段的两次synchronized引起了我的注意,这不是单例模式
中的DCL(double check lock)嘛 :D
大部分人肯定都会写,并且也知道为什么要使用DCL,可是都知道DCL还有一段小历史吗? DCL
其实在JDK1.5之前是有问题的,之道JDK1.5才修复完毕.
1 | class DCLSingleton { |
上面这段标准的DCL使用了关键字volatile
,也正是这个关键字,才引入了DCL的问题.
在JDK1.5之前volatile
变量的赋值过程,是有可能被reorder的.
JMM
或者说Java编译器,会根据自己的判断,在当前线程不影响最终结果的时候,对语句进行重排,这点大家都应该之道; 其中JDK1.5之前的volatile
变量的赋值,也会被重排…
比如上面代码中的_instance=new DCLSingleton()
,这个赋值过程,会被拆分为如下几个步骤:
- 新建对象
- 为新对象赋初值
- 将新对象assgin给变量
其实在单线程环境下,JMM会打断顺序,并且不会对最终结果有任何影响;比如:
- 新建对象
- 将新对象assgin给变量
- 为新对象赋初值
但是在多线程环境下,这种reorder就会带来问题,即第一个线程运行到第二次check lock的时候,在赋值的第二步,将还没有完成初始化的新对象直接assign给变量,就被切换到另外一个线程了,这时第二个线程直接将这个未完成初始化的对象直接返回,这就造成了问题…
这个问题知道JDK1.5才解决, 请看JSR133