找bug的一天

  • 神奇的bug
  • spring中的单例
  • 场景
  • 代码
  • 产生的问题
  • 分析
  • 进一步分析
  • 解决问题
  • 参考链接
  • 说明

神奇的bug


前言:我写的明明是单例,可是为什么初始化了二次?


今天写的这个bug和单例设计模式有关。 所谓单例设计模式,这个不多说,详情可以自行百度。

spring中的单例

springboot的control就是单例的,关于这个可以看看这个文章
spring的controller默认是单例还是多例

场景

在我这次的项目中遇到这样一个功能需求:我需要设计一个单例的类,这个类里面有个阻塞队列。
日志的生产和消费就需要放置在队列中。
日志的生产我会监听logback的事件,如果logback打印日志,就把打印的内容发送到队列里面。
然后webscoket消费数据。
说白了,这里面的队列需要单例的,而且起到中转数据的作用。

代码

这是对队列进行一个简单的封装,如下

package cn.xiejx.jfun.config.websocket;import cn.xiejx.jfun.vo.LogMessage;import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;/*** 创建一个阻塞队列,作为日志系统输出的日志的一个临时载体* @author jie* @date 2018-12-24*/
public class LoggerQueue {/*** 队列大小*/public static final int QUEUE_MAX_SIZE = 10000;private static LoggerQueue alarmMessageQueue = new LoggerQueue();/*** 阻塞队列*/private BlockingQueue blockingQueue = new LinkedBlockingQueue<>(QUEUE_MAX_SIZE);private LoggerQueue() {System.out.println("loggerquque:"+this.getClass().getClassLoader().toString());}public static LoggerQueue getInstance() {return alarmMessageQueue;}/*** 消息入队* @param log* @return*/public boolean push(LogMessage log) {return this.blockingQueue.add(log);}/*** 消息出队** @return*/public LogMessage poll() {LogMessage result = null;try {result = (LogMessage) this.blockingQueue.take();System.out.println("消费一条消息"+result);} catch (InterruptedException e) {e.printStackTrace();}return result;}
}

产生的问题

其实上面这个代码是没问题的。
我的项目是springboot,上面的单例使用的是没有用spring注解实现(springboot也有可以使用注解来使用单例)。
然后这边消费数据的时候调用take方法。
take方法没有数据是会阻塞的。
而数据已经有add进去了。
按理说可以消费到数据才是,可是数据却没有被消费。

分析

使用了java的一些分析工具,把堆dump了出来。
看到了消费数据的线程是处于等待状态。
最傻的方法用System.out.println也分析出线程一直在阻塞中(卡在take那里)

进一步分析

我确信单例的实现是没有问题的,按理说,数据add进去,应该是可以消费到的才对。
断点继续分析,不看不知道。
发现了消费和生产数据的时候,getInstance返回的对象的hashCode码不一样。
也就是说单例被破坏了,生产数据放在了a队列里面,而消费数据却要在b队列里面拿数据。
为什么呢?我的代码明明是单例啊啊啊啊!!!

解决问题

分析,如果是相同的类加载器,不可能出现这种情况。
那么我就在初始化的时候加上下面的代码

        System.out.println("loggerquque:"+this.getClass().getClassLoader().toString());

发现这个类果然被初始化了2次。确实不是‘单例’了(不同的类加载器分别初始化)。
输出如下:

....
loggerquque:sun.misc.Launcher$AppClassLoader@18b4aac2
....
loggerquque:org.springframework.boot.devtools.restart.classloader.RestartClassLoader@2a5810d2

原来是这个热部署的锅!!!
拨云见日!!!
pom.xml文件果断去除热部署依赖先(暂时我只能这么解决)

 <!--热部署工具--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><version>2.0.2.RELEASE</version><optional>true</optional><scope>runtime</scope></dependency><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><!--配置热启动--><fork>true</fork></configuration></plugin></plugins>

重启idea,问题解决。

参考链接

  • 在java中写出完美的单例模式
  • spring的controller默认是单例还是多例
  • 友情链接本人小站

说明

上面的代码非原创,来自大佬的项目。
码云:https://gitee.com/elunez/eladmin-qt
github:https://github.com/elunez/eladmin-qd
这里感谢作者的开源。

java单例实例对象在springboot中实例化了2次,原因竟然是热部署的锅(记一次神奇的bug)相关推荐

  1. java单例设计及其在jdk中的应用

    什么是单例 单例是要解决一个类只能有一个对象的问题,为什么要做这个限制? 单例如何实现 如何实现单例,把constructor私有化,不能new了,此时一个实例都没有了 constructor虽然私有 ...

  2. 【Kotlin】Kotlin 单例 ( 懒汉式 与 恶汉式 | Java 单例 | Kotlin 单例 | 对象声明 | 伴生对象 | get 方法 | ? 与 !! 判空 )

    文章目录 I . 单例的懒汉式与恶汉式 II . Java 中的懒汉式与恶汉式 III . Kotlin 中对应 Java 的懒汉式与恶汉式 IV . Kotlin 对象 ( object ) 声明 ...

  3. java 单例 内存释放_周小抒 – 梦想仗剑走天涯 | Android中关于Context单例模式引起的内存泄漏以及解决方案...

    内存溢出与内存泄漏的区别: 内存溢出是由于应用所消耗的内存或者应用申请的内存超出了虚拟机分配的内存,也就是内存不够用了. 内存泄漏是某个不再使用对象由于被其他实例引用,导致不能被GC回收,而导致的内存 ...

  4. java injector_java – 如何从Guice Injector获取所有单例实例?

    有没有一种简单的方法来枚举Guice Injector已经创建的所有单例实例?或者另外一种方法来获得实现特定接口的所有单例? 我想找到所有实现java.io.Closeable的单例实例,这样当我的服 ...

  5. java 单例设计模式 [

    为了保证类在内存中只能有一个对象,构造函数需要private外面不实例化对象,提供一个公共方法返回一个实例 //饿汉式 class Single{ //需要是static的,getInstance方法 ...

  6. Java单例的常见形式

    2019独角兽企业重金招聘Python工程师标准>>> Java单例的常见形式 本文目的:总结Java中的单例模式 本文定位:学习笔记 学习过程记录,加深理解,便于回顾.也希望能给学 ...

  7. java单例设计模式_Java设计模式之单例模式详解

    在Java开发过程中,很多场景下都会碰到或要用到单例模式,在设计模式里也是经常作为指导学习的热门模式之一,相信每位开发同事都用到过.我们总是沿着前辈的足迹去做设定好的思路,往往没去探究为何这么做,所以 ...

  8. java 单例加锁方法的讨论

    //2014.8.26  review 一个经典 DCL 问题 public class Singleton { private static Singleton instance=null; pub ...

  9. mongodb java 单例_JAVA单例MongoDB工具类详解

    shasha 2018年09月07日 681 0 JAVA单例MongoDB工具类 JAVA驱动版本: org.mongodb mongo-java-driver 3.0.2 工具类代码如下: pac ...

最新文章

  1. 容器(Collection/Map)、容器中的设计模式
  2. html5如何传递值,如何将var中的值传递到另一个var
  3. C语言试题八十一之利用递归函数调用方式,将所输入的5个字符,相反顺序打印
  4. RE-Base64编码分析
  5. The “FixedStepDiscrete“ solver cannot be used to simulate block diagram ‘foc_ruizi‘ because it conta
  6. 水星d128路由器虚拟服务器,幻影D128路由器怎么设置?
  7. win10 校验sha256
  8. pgadmin mac卸载_Mac软件卸载——安全彻底地在Mac上卸载Microsoft Outlook - Mac迪迪卫...
  9. 用Python画一颗心、小人发射爱心(附源码)
  10. mysql 下 计算 两点 经纬度 之间的距离 含具体sql语句
  11. 华为Freelace pro音质突然下降的解决方案
  12. 机器学习之梯度提升决策树(GBDT)
  13. 后端工程师-软件类OPPO-2023届校园招聘笔试题-后端(A卷)
  14. scram-sha1
  15. 深入理解Java虚拟机 - 字节码指令集
  16. 原生JS实现简单放大镜效果
  17. VS 2010 中的 VB.NET 编程
  18. Qml控制多个窗口任务栏图标显示及更改图标
  19. 什么是NFS?NFS挂载
  20. 复合函数的结合律证明

热门文章

  1. OpenGL ES实现三棱锥纹理贴图
  2. SRS:流媒体服务器如何实现负载均衡
  3. NIST Cybersecurity White Paper 2021
  4. 爱回收上市进程加速:原云集CFO陈晨加盟,转转等强敌环伺
  5. 一体化大威海的战略抉择
  6. Python要想学得好,【容器/可迭代对象/迭代器/生成器】少不了,稳扎稳打学Python!
  7. matlab绘图去白边
  8. flutter菜鸟教程!mysql半同步的主从搭建方式
  9. Java实现 计蒜客 拯救行动
  10. 程序员养娃记:撸一手好代码,却带不好一个娃?!