欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

三个糟糕的八股文,竟然变成了两个场景题目,让我感到十分惊讶!特别是第二个场景...

最编程 2024-01-22 14:01:03
...

这个场景就简单很多了。

当时有个小伙伴在群里扔了一个截图:

需要注意的是, if(!lock) 他截图的时候是给错了,真实的写法是 if(lock),lock 为 true 的时候就是加锁成功,进入 if。

同时这个代码这一行是有事务的:

写一个对应的伪代码是这样的:

if(加锁成功){
    try{
        //save有事务注解,并且确认调用的service对象是被代理的对象,即事务的写法一定是正确的
        return service.save();
    } catch(Exception e){
        //异常打印   
    } finally {
        //释放锁
        unlock(lockKey);
    }
}

就上面这个写法,先加锁,再开启事务,执行事务方法,接着提交事务,最后解锁,反正歪师傅横看竖看是没有发现有任何毛病的。

但是提供截图的小伙伴是这样描述的。

当他是这样写的时候,从结果来看,程序是先加锁,再开启事务,执行事务方法,然后解锁,最后才提交事务:

当时我就觉得:这现象完全超出了我的认知,绝不可能。

紧接着他提供了第二张截图:

他说这样拆开写的时候,事务就能正常生效了:

这两个写法的唯一区别就是一个是直接 return,一个是先返回了一个 resultModel 然后在 return。

在实际效果上,我认为是没有任何差异的。

但是他说这样写会导致锁释放的时机不一样。

我还是觉得:

然而突然有人冒出来说了一句: try 带着 finally 的时候,在执行 return 语句之前会先执行 finally 里面的逻辑。会不会是这个原因导致的呢?

按照这个逻辑推,先执行了 finally 里面的释放锁逻辑,再执行了 return 语句对应的表达式,也就是事务的方法。那么确实是会导致锁释放在事务执行之前。

就是这句话直接给我干懵逼了,CPU 都快烧了,感觉哪里不对,又说不上来为什么。

虽然很反直觉,但是我也记得八股文就是这样写的啊,于是我开始觉得有点意思了。

所以我搞了一个 Demo,准备本地复现一下。

当时想着,如果能复现,这可是一个违背直觉的巨坑啊,是一个很好的写作素材。

可惜,没有复现:

最后这个哥们也重新去定位了原因,发现是其他的 BUG 导致的。

另外,关于前面“try 带着 finally”的说法其实说的并不严谨,应该是当 try 中带有 return 时,会先执行 return 前的代码,然后把需要 return 的信息暂存起来,接着再执行 finally 中的代码,最后再通过 return 返回之前保存的信息。

这才是写在八股文里面的正确答案。

要永远牢记另一位伟人说过:实践是检验真理的唯一标准。

遇事不决,就搞个 Demo 跑跑。

关于这个场景,其实也很简单,拆开来看就是关于事务和锁碰撞在一起时的注意事项以及 try-return-finally 的执行顺序这两个基础八股而已。

但是当着两个糅在一起的时候,确实有那么几个瞬间让我眼前一黑,又打得我一脸懵逼。

最后,事务和锁碰撞在一起的情况,上个伪代码:

@Service
public class ServiceOne{
    // 设置一把可重入的公平锁
    private Lock lock = new ReentrantLock(true);
    
    @Transactional(rollbackFor = Exception.class)
    public Result  func(long seckillId, long userId) {
        lock.lock();
        // 执行数据库操作——查询商品库存数量
        // 如果 库存数量 满足要求 执行数据库操作——减少库存数量——模拟卖出货物操作
        lock.unlock();
    }
}

如果你五秒钟没看出这个代码的问题,秒杀这个问题的话,那歪师傅推荐你个假粉丝看看这篇文章::《几行烂代码,我赔了16万。》

好了,就酱,打完收工~