最近在阅读 weblech 的源代码的时候,发现一个写法有点意思。
public void saveCheckpoint()
{
SpiderConfig config = SpiderConfigInit.getSpiderConfigInit().getSingleSpiderConfig();
long intervalMillis = 1000 * config.getCheckpointInterval();
if(System.currentTimeMillis() - lastCheckpoint > intervalMillis)
{
synchronized(saveObj)
{
if(System.currentTimeMillis() - lastCheckpoint > intervalMillis)
{
writeCheckpoint(saveObj,saveFileName);
lastCheckpoint = System.currentTimeMillis();
}
}
}
}
这个函数位于一个线程类的内部,用来完成定时存档工作。它有两层 if 结构,中间有一个同步的操作。
隐约记得之前在《UNIX 环境高级编程》一书中看到过类似的用法,所以这里很快就知道了为什么这么写。
分析的思路如下:
假设结构为 if{synchronized}的结构,则可能在两个线程都进入if后,只有一个线程能进入锁并进行处理,而这个线程可能做了某些事,使得if条件不成立,但这时另一个线程已经通过了if,但这是编程人员在逻辑上不允许的,因为程序员应该保证每一步所产生的影响在可控范围内,否则,程序的逻辑则是模糊的,其运行的结果是无法预计的。所以,不可以为这个逻辑结构,除非你确定两个线程的操作对于再次进入 if 是没有影响的。
再假设,当结构为synchronized{if}的结构时会怎样呢?这其实很明显,如果一开始就同步,这会让不满足条件的线程耗费不必要的时间花在等待锁上,这样程序的效率就低了一点,所以要先判断它是否满足条件。
综上,所以结构是 if {synchronized if {}} 的结构。
我们再从正面来分析一下,这样写的好处。当存在多个线程会调用这个函数时,就会有线程进入最里层的 if ,记为 A,而还有线程卡在最外层的 if 的外面进不来。当A写存档之后,此时,正确的逻辑应该是其它的线程在时间间隔内不再需要写存档。所以,它会更改 lastCheckpoint 这个值,当卡在最外面的线程获得锁时,它又被卡在了最里面一个 if 的外面,它会直接返回,正好确实不需要写存档了。