代码如下:
static int GetFirstOrDefault(ThreadSafeListint 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。