美文网首页
并发情况下的单例模式

并发情况下的单例模式

作者: Y_Q | 来源:发表于2017-12-28 14:58 被阅读15次

    背景:有多个客户端会同时上传压缩包至平台,压缩包中有多个txt文件,平台会逐一解析然后将他们的数据入库。

    但是,在实践中发现,经常会有数据没有写入数据库的现象发生。但是log记录的是平台对其有进行分析的。

    那么问题的原因就可以缩小到“数据解析”->"数据入库”的环节。

    我的代码中, 数据解析的类是StabilityPerfReportHandler.java,数据入库的类是StabilityPerfReportDao.java

    先看一下整体的代码结构

    因为有多个文件,如果等到文件解析完再返回客户端一个值,则容易引起返回超时,导致文件重传。所以在Handler中是采用多线程对每个文件进行数据解析及入库的。那么就猜测,数据有解析但是没有写入数据库的原因是多线程同时调用了StabilityPerfReportDao.java中的入库函数,导致有冲突。

    那么我的第一个尝试是:给每个写入数据库的函数加锁

    原先:

    public void insertCpuData(List listCpuResults)

    改后:

    public synchronized void insertCpuData(List listCpuResults)

    事实证明,这种修改是没有作用的。

    但是可以肯定的一点就是在写入数据库的时候是存在冲突问题的,导致数据写入的时候,有些数据没写入丢失。

    再次梳理了一下代码,发现在解析完数据要写入数据库时采用的是单例的形式

    StabilityPerfReportDao.getInstance().insertCpuData(stabilityPerfCPUPos);

    查看了StabilityPerfReportDao的单例代码,发现是如下的:

    public static StabilityPerfReportDao getInstance() {

            if (null == stabilityPerfReportDao) {

                synchronized (StabilityPerfReportDao.class) {

                    if (null == stabilityPerfReportDao) {

                    stabilityPerfReportDao = new StabilityPerfReportDao();

                    }

                }

            }

           return instance;

        }

    查阅了相关资料,发现这段代码在多线程并发的情况下是有问题的。

    最根本的原因是,java寄存器的读写是无序的

    当我们new一个对象的时候,java是经历以下三个步骤的:

    a.给实例分配内存

    b.初始化构造器(构造器的作用就是给变量赋值)

    c.将引用指向分配的对存

    我们的期望是a->b->c,但是由于java寄存器的读写是无序的,所以他可能的顺序是a->c->b。如果顺序是a->c->b,那么我们获得的对象是没有被初始化的,这样就会产生问题。

    结合到我们上述有问题的代码来看,就是以下这样的 

    a.线程A、B同时调用StabilityPerfReportDao.getInstance()

    b.线程B先进入,判断到StabilityPerfReportDao为空,则进入到synchronized模块中,new一个StabilityPerfReportDao对象

    c.此时线程A进入,判断到StabilityPerfReportDao不为空,便返回了这个instance,但是这个instance是还没有经过构造器初始化的,所以线程A拿到这个instance去执行接下来的操作会出现问题。

    现在问题找到了,就要相处解决的方法了,以下是我解决的代码

    private static StabilityPerfReportDao instance = new StabilityPerfReportDao();

    public static StabilityPerfReportDao getInstance() {

            return instance;

        }

    在类加载的时候,就去定义一个静态变量instance并初始化它。接下来每个外部类调用getInstance去获得唯一备份的instance。代码这么修改之后,就完美地解决了问题了。

    相关文章

      网友评论

          本文标题:并发情况下的单例模式

          本文链接:https://www.haomeiwen.com/subject/lltpgxtx.html