0 0

Java单例模式中偶然出现的诡异NPE15

以下是一个应用类的简化代码,最近在这段代码的19行中出现了NPE,说tmpValueMap为null。这个工具类已经使用了几年,只出现过一次NPE,没再能重现,也不确定原因。
查过些资料,提到类属性的初始化会在构造函数执行前完成,那tmpValueMap不应该为null。即使这个说明不对,那也应该是16行的asynChecker先抛出NPE才对。google了一些类似的问题,但都不是这种偶然才会出问题的情况。
想不到合理的解释,特上来请教,望能指点一下。

import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;


public class Singleton {
	
	private static Singleton instance = new Singleton();
	
	private Timer asynChecker = new Timer(true);
	
	private Map<Integer, String> tmpValueMap = new HashMap<Integer, String>();
	
	private Singleton() {
		asynChecker.schedule(new TimerTask() {
			@Override
			public void run() {
				tmpValueMap.clear();
			}
		}, 5000, 5000);
	}
	
	public static Singleton getInstance() {
		return instance;
	}
}

问题补充:
asyty 写道
呃 貌似这代码不会出现问题的啊

要是19行出现问题了,有可能构造什么的都正常的,是在之后不断执行task的过程中,其他的代码里对tmpValueMap操作修改成了null


这个是有确认过的,tmpValueMap是私有成员,类内部也没有代码将它置为null

问题补充:
1v1_问天 写道
现在还有这个问题吗?

实际上,这个代码用了好几年,目前为止,只出现过一次NPE,没有办法再重现,堆栈记录里也只是简单的说19行NullPointerException。

问题补充:
huoyj 写道
类加载的时候静态属性会最先执行的,所有run执行的时候可能会出你说的问题,建议你这样写:

private Map<Integer, String> tmpValueMap = null; 
     
    private Singleton() { 
        this. tmpValueMap = new HashMap<Integer, String>();
        asynChecker.schedule(new TimerTask() { 
            @Override 
            public void run() { 
                tmpValueMap.clear(); 
            } 
        }, 5000, 5000); 
    } 


我细看了一下Java编程思想,里面提到:无法阻止自动初始化的进行,它将在构造器被调用之前发生。(V4_zh,94p)
我理解为进入构造函数的时候,tmpValueMap=new ...是已经执行了的,所以不会为null。

问题补充:我把代码信息再补充一下,当时的异常信息是同事截了个图过来的,我抄写在下面,行号和给出的类代码重新对应过。
import java.util.HashMap;  
import java.util.Map;  
import java.util.Timer;  
import java.util.TimerTask;  

   
public class Singleton {
       
    private static Singleton instance = new Singleton();  
       
    private Timer asynChecker = new Timer(true);  
      
    private Map<Integer, String> tmpValueMap = new HashMap<Integer, String>();  
      
    private Singleton() {  
        asynChecker.schedule(new TimerTask() {  
            @Override  
            public void run() {  
                checkDisabledPools();  
            }  
        }, 5000, 5000);  
    }  
      
    public static Singleton getInstance() {  
        return instance;  
    }

    private void checkDisabledPools() {
        if (disabledPools.isEmpty())
			return;
        /*以下省略*/    
    }

}



Exception in thread "Timer-0" java.lang.NullPointerException
at com...Singleton.checkDisabledPools(Singleton.java:29)
at com...Singleton.access$000(Singleton.java:7)
at com...Singleton$1.run(Singleton.java:25)
at java.util.TimerThread.mainLoop(Timer.java:512)
at java.util.TimerThread.run(Timer.java:462)

问题补充:
asyty 写道
引用

private Map<Integer, String> tmpValueMap = new HashMap<Integer, String>();  

if (disabledPools.isEmpty()) 


哪来的disabledPools?


不好意思,改得不完整,应该是
private Map<Integer, String> tmpValueMap = new HashMap<Integer, String>();   

if (tmpValueMap.isEmpty())  


问题补充:
long502147 写道
import java.util.HashMap;

public class Singleton {

private static Singleton instance = new Singleton();

private Timer asynChecker = new Timer(true);

private Map<Integer, String> tmpValueMap = null;

private Singleton() {
                  tmpValueMap = new HashMap<Integer, String>();
asynChecker.schedule(new TimerTask() {
@Override
public void run() {
tmpValueMap.clear();
}
}, 5000, 5000);
}
}
有没有试试改成这样呢


改成这样也可以的,我主要是想了解一下异常的原因,避免下次又写错
2011年10月24日 19:46

18个答案 按时间排序 按投票排序

0 0

这个问题的本质是由于timer里面的线程没有从主存中同步状态过来,你这个是2个线程,timer里面的执行线程没有从主存中及时同步 tmpValueMap这个值,你两个线程共享变量也没有用到synchronized同步快,也没有用 volatile 关键字修饰,能不出问题吗?在没有这个同步的条件下本地线程什么时候从主存同步状态过来是不确定的。建议看看java内存模型和java并发编程实战那本书。里面都讲了这个。这个问题在tmpValueMap变量前用volatile修饰下就出每次主动同步状态到本地线程,但是还是不是线程安全的,你两个线程共享同一个变量既然不用同步?这样会出现数据不一致的情况。可以换成jdk里面的支持并发map替代这个hashMap。


private Map<Integer, String> tmpValueMap = new HashMap<Integer, String>(); 

//换成如下就不会出现问题了
private volatile Map<Integer, String> tmpValueMap = new ConcurrentHashMap<Integer, String>(); 

2011年11月06日 13:19
0 0

会不会执行顺序的问题,因为构造函数没有执行完成,tmpValueMap还没有被new出来,那样在run函数里面调用tmpValueMap就会发生错误,我记得有一本书里面提醒不要把未构造完成的对象的引用传递出去,不知道对不对。

2011年11月03日 17:20
0 0

tmpValueMap 非线程安全

2011年11月02日 09:53
0 0

建议楼主搜索下代码,看代码里面是否有使用反射修改了这个tmpValueMap = null;
比如:

Singleton s = Singleton.getInstance();
Field f = Singleton.class.getDeclaredField("tmpValueMap");
f.setAccessible(true);
f.set(s, null);

我仅仅是一种猜想,其它还有什么情况我暂时没有想到~~

2011年10月31日 15:07
0 0

呃 前面多打了个new

Map<Integer, String> tmpValueMap = Collections.synchronizedMap(new HashMap<Integer, String>());

Map<Integer, String> tmpValueMap = new ConcurrentHashMap<Integer, String>();

2011年10月28日 10:18
0 0

原先的代码应该没啥问题,要是把map改成线程安全的就更好了。。。。

 Map<Integer, String> tmpValueMap = Collections.synchronizedMap(new new HashMap<Integer, String>());

或者
 Map<Integer, String> tmpValueMap = new ConcurrentHashMap<Integer, String>();

2011年10月28日 10:16
0 0

Map不是线程安全的,会造成Map内部死循环、数据异常等情况,可以使用ConcurrentHashMap

2011年10月28日 07:47
0 0

引用
这个singleton可能不是线程安全的。

静态初始化应该是安全的,不过那个map不是线程安全的

可以用 Map<Integer, String> tmpValueMap = Collections.synchronizedMap(new new HashMap<Integer, String>()) 构造线程安全的Map

不过不知道NullPointException和这个有没有关系

2011年10月26日 08:45
0 0

引用

有没有试试改成这样呢


定义变量时赋值,是在构造函数执行之前的,所以 两者的结果应该没啥区别

2011年10月26日 08:36
0 0

这个singleton可能不是线程安全的。

2011年10月26日 03:04
0 0

import java.util.HashMap;  
import java.util.Map;  
import java.util.Timer;  
import java.util.TimerTask;  

   
public class Singleton {
       
    private static Singleton instance = new Singleton();  
       
    private Timer asynChecker = new Timer(true);  
      
    private Map<Integer, String> tmpValueMap = null;      
    private Singleton() {  
       tmpValueMap = new HashMap<Integer, String>();  

        asynChecker.schedule(new TimerTask() {  
            @Override  
            public void run() {  
                checkDisabledPools();  
            }  
        }, 5000, 5000);  
    }  
      
    public static Singleton getInstance() {  
        return instance;  
    }

    private void checkDisabledPools() {
        if (disabledPools.isEmpty())
			return;
        /*以下省略*/    
    }

}

有没有试试改成这样呢

2011年10月25日 18:33
0 0

import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;


public class Singleton {

private static Singleton instance = new Singleton();

private Timer asynChecker = new Timer(true);

private Map<Integer, String> tmpValueMap = null;

private Singleton() {
                  tmpValueMap = new HashMap<Integer, String>();
asynChecker.schedule(new TimerTask() {
@Override
public void run() {
tmpValueMap.clear();
}
}, 5000, 5000);
}

public static Singleton getInstance() {
return instance;
}
}
有没有试试改成这样呢

2011年10月25日 18:30
0 0

引用

private Map<Integer, String> tmpValueMap = new HashMap<Integer, String>();  

if (disabledPools.isEmpty()) 


哪来的disabledPools?

2011年10月25日 16:27
0 0

几年才出现一次的bug,而且还是这种超级诡异的问题。。。我看你还说直接放过吧 如果不是特别特比重要的原因 没必要花太大精力

2011年10月25日 16:25
0 0

现在还有这个问题吗?

2011年10月25日 12:18
0 0

建议你检查下其他的代码 应该不是这段代码的问题

2011年10月25日 11:16
0 0

呃 貌似这代码不会出现问题的啊

要是19行出现问题了,有可能构造什么的都正常的,是在之后不断执行task的过程中,其他的代码里对tmpValueMap操作修改成了null

2011年10月25日 11:15
0 0

类加载的时候静态属性会最先执行的,所有run执行的时候可能会出你说的问题,建议你这样写:

private Map<Integer, String> tmpValueMap = null; 
     
    private Singleton() { 
        this. tmpValueMap = new HashMap<Integer, String>();
        asynChecker.schedule(new TimerTask() { 
            @Override 
            public void run() { 
                tmpValueMap.clear(); 
            } 
        }, 5000, 5000); 
    } 

2011年10月25日 08:20

相关推荐

    JAVA8如何妙用Optional解决NPE问题详解

    Java 8 引入了 `Optional` 类,旨在帮助开发者更优雅地处理可能出现 `null` 值的情况,从而减少 `NullPointerExceptions`(NPEs)的发生。`Optional` 是一个容器类,它可能包含或者不包含非 `null` 值。如果值存在则...

    NPE.rar_MATLAB实现NPE_NPE matlab_npe

    标题中的"NPE.rar"指的是一个使用MATLAB编程语言实现的NPE(Non-Photorealistic Rendering,非真实感渲染)的代码压缩包。NPE是一种计算机图形学技术,它允许艺术家和开发者创建出具有绘画风格或者模拟特定艺术媒介...

    NPE.rar_NPE人脸识别_人脸 数据集_流型_流型人脸识别_流型识别

    《流型学习在人脸识别中的应用——以NPE算法为例》 人脸识别技术,作为生物特征识别领域的重要组成部分,近年来发展迅速,广泛应用于安全监控、身份验证等多个领域。在这些技术中,流型学习(Manifold Learning)是...

    npe.rar_npe

    标题中的“npe.rar_npe”可能是指与NPE(Network Processing Element)相关的RAR压缩文件,这通常在处理网络数据流或协议解析时使用。NPE是某些硬件平台中的一个组件,负责处理网络相关的计算任务,提高系统效率。在...

    npe.m.rar_Matlab代码_npe_npe.m

    亲测可以正常运行的邻域保持嵌入算法NPE的程序,欢迎大家下载。

    NPE.rar_npe

    同时,NPE也可应用于异常检测、聚类分析等任务,帮助发现数据的潜在模式和异常行为。 NPE.m文件可能是一个MATLAB程序,用于实现NPE算法。用户可以利用这个程序对给定的数据集进行降维操作,观察和分析数据的结构...

    ISOPAM LLE,NPE等降维算法

    流形学习是机器学习领域中的一个重要分支,它旨在从高维数据中发现低维的、非线性的结构,即所谓的“流形”。在处理高维数据时,由于数据可能存在冗余或噪声,直接进行分析可能会导致信息丢失或误导。因此,降维技术...

    NPE_npe_dataembedding_

    NPE的基本原理是基于一个假设:在高维空间中的数据点,如果它们在原始空间中是近邻,那么在低维空间中也应该保持这种邻接关系。为了实现这一目标,NPE利用了拉普拉斯矩阵,这是一种刻画数据点之间相互关系的工具。...

    TonPE_XP_V1.9.4通用PE

    TonPE_XP_V1.9.4通用PE

    JAVA中常见的异常

    ### JAVA中常见的异常知识点详解 #### 一、`java.lang.NullPointerException` 在Java编程中,`java.lang.NullPointerException`(简称NPE)是最常见的异常之一。这种异常通常发生在尝试访问一个空对象引用的方法或...

    锐捷NPE50系列网络出口引擎

    锐捷NPE50系列网络出口引擎的介绍

    最新PE工具(TonPE)v3.3 --教你如何制作.

    系统维护工具 最新PE工具(TonPE)v3.3 --教你如何制作.doc

    基于NPE改进算法的人脸识别.pdf

    NPE通过寻找低维空间中的映射,使得高维数据的邻域关系在低维空间中尽可能保持不变。然而,NPE算法存在两个主要问题:一是使用欧式距离选取近邻点可能导致重构误差;二是线性重构过程中未考虑样本点间的类别信息,这...

    阿里巴巴泰山版java开发手册.zip

    手册推荐在合适的地方使用单例、工厂、观察者等经典设计模式,以实现可扩展和可维护的代码结构。 5. **并发编程**:Java的并发编程是性能优化的关键。手册提供了线程安全、锁机制、并发容器等使用指导,如避免过度...

    阿里巴巴Java开发手册1.4.0(详尽版).pdf

    7. **设计模式**:提倡使用设计模式来解决常见问题,如工厂模式、单例模式、观察者模式等。理解并合理运用设计模式能提升代码的可扩展性和可维护性。 8. **代码结构**:模块化设计,每个类、方法职责单一,遵循“高...

    面试中曾被问到的Java Question.pdf

    - **单例模式**:实现方式的比较,如懒汉式、饿汉式、枚举等。 - **工厂模式**:简单工厂、工厂方法、抽象工厂的区别。 - **装饰者模式**:动态地给一个对象添加新的功能而不改变其结构。 ### 5. 框架与工具 - **...

    Java学习 常用命令 常用单词

    Java学习常用命令常用单词知识点总结 ...* Java中的文件操作类:包括File类、InputStream类、OutputStream类等。 Java学习中的常用命令和单词是非常重要的,了解这些知识点可以帮助开发者更好地学习和应用Java语言。

    论文研究-相关NPE算法的人脸识别研究.pdf

    传统的近邻保持嵌入(NPE)算法采用欧氏距离作为近邻点选取的度量,但欧氏距离只表示两点间的直线距离,在高维空间中不一定能反映数据间的真实空间分布,易导致近邻选取不准确。针对此问题,提出了相关近邻NPE(CNPE...

    java出现404的原因是_关于出现404错误的原因 关于出现500错误的原因

    清空浏览器缓存,或者尝试在隐私模式下访问,看看是否仍然出现404。 解决404错误的一般步骤包括: 1. 检查URL。 2. 查看web.xml配置。 3. 确保后台代码中的转发或重定向正确无误。 4. 清理项目缓存,例如在Eclipse...

Global site tag (gtag.js) - Google Analytics