基于Java HashMap的死循环的启示详解

 更新时间:2013年05月13日 11:43:23   作者:  
本篇文章是对Java HashMap的死循环进行了详细的分析介绍,需要的朋友参考下

一、单线程改造为多线程也是个技术活

正如我们看到耗子叔叔博客里写的那样,原来是单线程的应用程序,”后来,我们的程序性能有问题,所以需要变成多线程的,于是,变成多线程后到了线上,发现程序经常占了100%的CPU“。

考虑到是淘宝的工程师曝出来的问题,他们的技术基础一般都很扎实,连他们都用错了,所以把单线程改造为多线程并不是想象中的那么简单,我认为。

你可能很不服气地反问,淘宝的工程师又怎么了,单线程改为多线程有什么难的?无非就是应用现有的多线程技术嘛,你看,我有非常强烈的线程安全意识,我知道同步、死锁、竞态条件,还知道lock free和线程安全容器,还知道各种线程安全同步构造……难道还写不出线程安全的应用程序?

实际情况是,线程安全的应用程序并不一定因为你有扎实的线程安全基础和开发经验就能够写好的。

试着举两个例子:

1、使用线程安全容器通过索引取数据

很多人知道的线程安全容器,实际使用的时候并不一定不出现BUG,下面的(有隐患的)代码就比较典型:

复制代码 代码如下:

        static int GetFirstOrDefault(ThreadSafeList<int> list)
        {
            if (list.Count > 0)
            {
                return list[0];
            }
            return 0;
        }

上面的函数参数list如果一开始传入一个元素总数为1的列表,大家能分析出上面的代码会有什么问题吗?

关于线程安全容器,之前我恰好也总结过一篇文章<深入线程安全容器的实现方法>。线程安全容器并不真正安全,上面有问题的代码就是出自于这里。

 

2、多线程操作邮件的失误

还有就是多线程应用场景的分析可能不正确,曾经因为一个邮件收发程序的性能问题,我也大胆改造过应用程序,改来改去就出现了重大BUG,

大家可以看看我痛心疾首总结过的<基于一个应用程序多线程误用的分析详解>。

 

上面举的这两个例子,我只是想说明,多线程应用程序中,因为线程安全产生的BUG其实是很微妙的,一个考虑不周或者认识不够深刻,出现问题的可能性简直防不胜防。

二、ReHash的代价

上面第一点主要是闲谈线程安全,接着我们也说说哈希表,深刻理解消耗成本很大的ReHash。

我们平常理解中的哈希表是“以空间换时间的一种数据结构”。这样说的太久了,大家可能会有一种直观上的错觉,就是哈希表牺牲的是空间,争取的是时间。

但是,ReHash的过程其实是空间和时间的双重重大损失,因为分析源代码,我们知道ReHash的过程其实就是一个动态扩容的过程,而哈希表的扩容是个空间和时间消耗都非常惊人的内部操作。

为什么说ReHash是个空间和时间消耗都非常惊人的内部操作呢?

1、原来当我们对哈希结构的容器进行扩容时,散列表内部要重新new一个更大的数组,然后把原来数组的内容拷贝到新数组,并进行重新散列;

2、new出来的这个更大的新数组容量有多大也是一门学问,一般来说,新数组的大小会设置成原数组双倍大小的相近的一个素数(.NET中这个素数的生成还有一定的技巧)。

从1和2这两点可以看出,ReHash的代价确实非常高。在不久以前我碰巧写过一篇关于.NET容器的动态扩容的文章<解析从源码分析常见的基于Array的数据结构动态扩容机制的详解>,其中也浅显总结了.NET的HashTable的扩容机制,现在对照Java中的HashMap源码,看到熟悉的ReHash函数命名,再看一遍.NET中的实现,果然有比较才能有提高。

至于我们平时所理解的“以空间换时间“,其实是指哈希具有O(1)复杂度的数据检索效率,但它受填充因子影响,空间开销通常很大,空间利用率不高。

所以我们常常说哈希表适用于读操作频繁,写操作较少应用场景,比如把哈希表当做缓存容器,于我心有戚戚焉。

最后看到这句“有人把这个问题报给了Sun,不过Sun不认为这个是一个问题。因为HashMap本来就不支持并发。要并发就用ConcurrentHashmap…”

根据实际开发经验,线程安全的容器并不真正线程安全,会用ConcurrentHashmap也只是进入初级阶段,同时忍不住要感慨下当年如日中天风光无限的Sun。

相关文章

  • SpringCloud中的分布式锁用法示例详解(Java+Redis SETNX命令)

    SpringCloud中的分布式锁用法示例详解(Java+Redis SETNX命令)

    在Spring Cloud项目中,使用Java和Redis结合实现的分布式锁可以确保订单的一致性和并发控制,分布式锁的使用能够在多个实例同时提交订单时,仅有一个实例可以成功进行操作,本文给大家介绍Spring,Cloud中的分布式锁用法详解(Java+Redis SETNX命令),感兴趣的朋友一起看看吧
    2023-10-10
  • springboot后端如何实现携带token登陆

    springboot后端如何实现携带token登陆

    这篇文章主要介绍了springboot后端如何实现携带token登陆,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • java中方法递归的简单示例

    java中方法递归的简单示例

    这篇文章主要给大家介绍了关于java中方法递归的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • Java中树的存储结构实现示例代码

    Java中树的存储结构实现示例代码

    本篇文章主要介绍了Java中树的存储结构实现示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • java验证电话号码的方法

    java验证电话号码的方法

    这篇文章主要介绍了java验证电话号码的方法,需要的朋友可以参考下
    2014-02-02
  • Java编译器用maven打war包出错解决办法

    Java编译器用maven打war包出错解决办法

    这篇文章主要介绍了用maven打war包出错的解决办法,需要的朋友可以参考下
    2018-03-03
  • Spring Security中使用authorizeRequests遇到的问题小结

    Spring Security中使用authorizeRequests遇到的问题小结

    Spring 是非常流行和成功的 Java 应用开发框架,Spring Security 正是 Spring 家族中的成员,这篇文章主要介绍了Spring Security中使用authorizeRequests遇到的问题,需要的朋友可以参考下
    2023-02-02
  • java报错Cause: java.sql.SQLException问题解决

    java报错Cause: java.sql.SQLException问题解决

    本文主要介绍了java报错Cause: java.sql.SQLException问题解决,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-08-08
  • 三种Java打印PDF文档的实例代码

    三种Java打印PDF文档的实例代码

    这篇文章主要介绍了三种Java 打印PDF文档的方法,文中代码非常详细,供大家学习和参考,感兴趣的朋友快来了解下
    2020-06-06
  • java使用IO流对数组排序实例讲解

    java使用IO流对数组排序实例讲解

    在本篇文章里小编给大家整理的是一篇关于java使用IO流对数组排序实例讲解内容,有兴趣的朋友们可以学习下。
    2021-02-02

最新评论