Java HashMap的死轮回的启发
添加时间:2013-5-14 点击量:
在酷壳上看到博主陈皓写的新文章疫苗:Java HashMap的死轮回。博主看题目很是透辟,代码解析到位,并且图文并茂,很轻易让人懂得一个死轮回是怎么产生的。
在博文中,耗子叔叔解析的最首要的一点就是HashMap在ReHash的这个过程中,因为多线程操纵容器,不测埠很隐蔽地机关了一个环形链接导致了死轮回(Infinite Loop)。
给我的启发简单总结如下:
一、单线程为多线程也是个技巧活
正如我们看到耗子叔叔博客里写的那样,本来是单线程的应用法度,”后来,我们的法度机能有题目,所以须要变成多线程的,于是,变成多线程后到了线上,发明法度经常占了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。
容易发怒的意思就是: 别人做了蠢事, 然后我们代替他们, 表现出笨蛋的样子。—— 蔡康永
在酷壳上看到博主陈皓写的新文章疫苗:Java HashMap的死轮回。博主看题目很是透辟,代码解析到位,并且图文并茂,很轻易让人懂得一个死轮回是怎么产生的。
在博文中,耗子叔叔解析的最首要的一点就是HashMap在ReHash的这个过程中,因为多线程操纵容器,不测埠很隐蔽地机关了一个环形链接导致了死轮回(Infinite Loop)。
给我的启发简单总结如下:
一、单线程为多线程也是个技巧活
正如我们看到耗子叔叔博客里写的那样,本来是单线程的应用法度,”后来,我们的法度机能有题目,所以须要变成多线程的,于是,变成多线程后到了线上,发明法度经常占了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。
容易发怒的意思就是: 别人做了蠢事, 然后我们代替他们, 表现出笨蛋的样子。—— 蔡康永