理想情况来讲,开发在开始编写代码之前就应该讲并发情况考虑进去,但是大多数实际情况确是,开发压根不会考虑高并发情况下的业务问题。主要原因还是因为业务极难遇到高并发的情况。
下面列举两个比较常见的后端编码中常见的并发BUG:
在Java应用程序中,server,controller,处理程序和存储库通常是单例的。它们是在应用启动时创建的,然后请求通常通过多个线程传递给它们。
代码如下:
public void handleOrder(Order order) {
...
currentLineItem = order.getLine(0);
processLineItem();
}
private void processLineItem() {
myService.store(currentLineItem);
}
这违反了两个原则:线程安全和有意义的对象状态。这里处理一个order
对象的时候只是处理了其中一个的currentLineItem
,先是赋值给了当前类对象的属性,然后去处理这个currentLineItem
对象,但是如果多个线程同时请求到当前的类单例对象,那么赋值和处理对象中间会发生第二次赋值,此时再去处理currentLineItem
对象很可能已经不是之前order
对象的currentLineItem
。
如果将请求的每个属性放入该请求的接收者中,那么将有两个风险:
延迟初始化允许:
由于以下原因,启动速度更快
虽然如此,但是,如下代码可能会发生错误:
private LazyService getLazyService() {
if (lazyService != null) {
return lazyService;
}
LazyService newLazyService = connectToLazyService();
registerWithServiceRegistry(newLazyService);
lazyService = newLazyService;
return newLazyService;
}
尽管它可以工作,但并发调用很可能出错。在示例中:
为了正确进行单例初始化,您应该使用双重检查锁定或使用框架,甚至使用基于static字段的简单Java单例初始化,如下:
private volatile static Singleton singleton;
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}