当前位置: 首页 > 科技观察

写了个全局变量bug,被同事打脸

时间:2023-03-13 14:43:13 科技观察

据说stackmanager前段时间写了一个函数,测试0个bug上线,上线后运行良好。好多天没人报BUG了,爽。.没有问题还好,一旦有问题,那就是大问题了。.最近有客户反馈了一些数据混乱的问题。见码生死,甚是奇异。仔细看代码,原来是全局变量的问题,导致并发情况下线程不安全。事后被同事打脸!!!谨慎使用全局变量。我在公司一直强调,从来没有想过这么低级的问题会发生在我身上。我真的很惭愧。.一开始,我使用Spring注入对象:@AutowiredprivateObjectobject;因为spring默认是单例的,这样写是没有问题的。后来随着业务的发展,需要多个不同的业务实例,所以改成了这个Method:@SetterprivateObjectobject;这个@Setter是Lombok的一个注解,用来生成setter方法。现在想想,真是低级。在同时操作的情况下,这个对象肯定会被覆盖,从而导致上面提到的问题。写了这么低级的bug,不怕不好意思贴出来,大家记住了。另外总结几个全局变量慎用的场景:1.SimpleDateFormatSimpleDateFormat禁止定义为静态变量或全局共享变量,因为它不是线程安全的,已经写进阿里巴巴的♂:为什么说SimpleDateFormat不是线程安全的?我们看一下它的format方法的源码:可以看到calendar变量也是一个全局变量,在多线程的情况下可能会设置脏变量。因此,如果要使用SimpleDateFormat,每次使用都要创建一个SimpleDateFormat对象,实现线程间的隔离。2.资源连接资源连接包括数据库连接、FTP连接、Redis连接等,全局变量也要慎用。如果大量使用全局变量,会遇到以下问题:对象的连接关闭,导致其他线程的业务中断;因为是全局变量,创建的时候可能会创建多个实例,关闭连接的时候可能只有一个对象的连接被关闭,导致其他连接没有关闭,最终导致对象不可用连接消耗系统;3.数值运算这也是一个很经典的问题。如果要使用多线程累加一个数等操作,不要使用全局基本类型的变量,如下所示Show:privatelongcount;在多线程的情况下,某个线程获取的值可能已经被其他线程修改了,最终获取的值会不准确。当然上面的例子可以通过加锁来解决,也可以使用全局原子类(java.util.concurrent.atomic.Atom*)来解决,例如:privateAtomicIntegercount=newAtomicInteger();注意这个原子类在使用全局变量时不存在线程安全问题,它使用CAS算法来保证数据的一致性。不过阿里推荐使用LongAdder,因为它性能更好:java.util.concurrent.atomic.LongAdder4,全局session看下面的例子:@AutowiredprotectedHttpSession;全局注入一个Session对象,在Spring中,这样的全局注入使用上面的是默认没有问题的,包括request和response对象,都可以通过全局注入获取。这样会不会有线程安全?不!这样,在初始化Bean的时候,Spring并没有注入真正的对象,而是注入了一个代理对象,真正使用的时候通过代理对象获取到真正的对象。而且,Spring在注入这类对象时,使用了线程局部变量(ThreadLocal),保证了request/response/session对象的线程安全。详情就不展开了。详细的介绍和测试可以点击此链接查看本文。既然是线程安全的,但是也得注意一下,如果我在方法中主动让session对象失效然后rebuild:session.invalidate();session=request.getSession();这样,session对象就变成了一个真正的对象,不再是代理对象,而是变成了我在文章开头提到的多线程安全问题。如果线上出现session混乱,用户A可能会看到用户B的数据。想一想,这不可怕吗?所以,即使可以这样使用,也得非常小心,最好在方法级别使用这些对象。今天总结一下,栈长总结了我是怎么写这个全局变量的low-levelbug的,同时也总结了4种慎用全局变量的情况。相信大家或多或少都遇到过类似的问题,希望能帮助大家少踩坑。全局变量虽然好,但我们还是要慎用。我们必须考虑它们是否会导致多线程安全问题,否则会导致重大问题。