呵呵,感觉转了一个弯:

Benq在以前的帖子中曾建议“使用Spring时,仔细考虑是否到底需要使用Singleton,能不用尽量不用。”

我们现在反过来看看,这句话也不尽合理吧?

是不是应该改为“慎重考虑所提供的bean类在实际应用中,多线程情况下的处理方式”,而不是一句简单的“能不用Singleton就不用Singleton”

本想等readonly来贴的,这两天也不知到哪去了,我就帮他贴一下吧,有兴趣的人可以看看吧,
readonly不会反对吧?readonly原来就是。。。嘿嘿,不说了:)
以下是readonly和Doug Lea的email(readonly copy了我),Doug Lea也是真够热心的,每次都很快就答复了。
我发现我也真够笨的,jdk1.5下面就带了源码的,我还去反编译干嘛:(
昨天看了下HashMap的源码,发现Doug Lea也是作者之一呀!


Dear Doug,
My friend forward your kindly reply to me (quoted below), we know
that the HashMap is not thread safe, but I'm curious about possibility
of using HashMap as a cache: if the concurrent putting happened, as
long as it putting the same key, the HashMap.get(key) method in other
reader threads should only return one of these concurrrent putting
objects, shouldn't throw any exceptions. Is it right?

public Long getTemperature(String city) {
Long result = (Long) _tempCache.get(city);
if (result == null) {
result = calcTemperature(city);
_tempCache.put(city, result);
}
return result;
}

Regards,
--Readonly


No. The internals of HashMap are not consistently read-only
concurrently accessible. Which means that you can possibly read
completely incorrect values while the table is being modified.

-Doug


Dear Doug,
Deeply appreciated your prompt reply.

I write some code, try to simulate the concurrent putting (detail
below), but can not get a completely incorrect value as you mentioned
, would you please specify any flaws in this code? My testing
environment is j2sdk1.4.1 on win2000.


import java.util.HashMap;
import java.util.Map;

/**
* @author Quake Wang
* @since 2004-11-1
* @version $Revision: 1.0 $
*/

public class HashMapCache {
private static final Map _cache = new HashMap();

public static void main(String[] args) {
for (int i = 0; i <= 10; i++) {
final Long temp = new Long(i);
new Thread() {
public void run() {
while (true) {
_cache.put(
"NY", temp);
}
}
}.start();
}

for (int i = 0; i <= 10; i++) {
new Thread() {
public void run() {
while (true) {
Long cachedTemp = (Long) _cache.get(
"NY");
//if concurrent putting break the cached value,
will we
// get a completely incorrect value?
if (cachedTemp.longValue() > 10 ||
cachedTemp.longValue() < 0) {
System.out.println(cachedTemp);
}
}
}
}.start();
}
}

}

Regards,


HashMap is not threadsafe, so you already know that there are some
sequences of actions will cause it to fail when accessed by multiple
threads. There is no reason for you to try to replicate them -- even
if some sequences work out OK, small variations of them will
not.

Instead, use a thread-safe map. The ConcurrentHashMap class has access
times that are about as fast as HashMap, but is known to be safe, so
you should use it.

-Doug

Doug Lea只是反复强调HashMap不是Thread safe, 要使用他写的ConcurrentHashMap, 偶尝试着写代码去模拟他说的可能会获得一个完全不同值的情况, 结果却实现不了, 偶的小脑袋是想不明白为什么了......

不使用同步哪里谈到什么同步问题?即使同步也不会形成什么严重的性能问题啊?单态的东西本身就不可能总是提供给你调用synchronized方法。需要做同步的方法调用率是很低的。

好久没看 发现原来大家还在这里:)

还是说几句吧。
有关“破坏结构”这个词,我认为是说对约定的数据结构规则的破坏
在只有一个key的时候size=2 也许你不care,对你没有什么影响 但是确确实实违反了对map的约定。这应该是没有什么辩驳的。
也许在这个应用里无关大局,但是也许在某些地方这就是定时炸弹。
目前除了size问题之外,不知道还没有其他的例子说明并发引起的问题?大家可以都想想。算是互相提高吧.
另外,Lea 是这方面的权威,觉得目前还是应该相信他,起码他的方法万无一失

许多没有正确同步的程序在某些情况下似乎工作得很好,例如在轻微的负载下、在单处理器系统上,或者在具有比 JMM 所要求的更强的内存模型的处理器上。
这篇文章也许有用 http://www-900.ibm.com/developerWorks/cn/java/j-jtp02244/

讨论这么多,还是用最近一期TSS上老外的话说:
"Singleton 是邪恶的",具体文章见:
http://www.pyrasun.com/mike/mt/archives/2004/11/06/15.46.14/index.html

在段落:Using the example with IoC中,第一个就是:Singletons are evil。

注意这篇文章是在最近发表的,可不是在我之前,否则又有人说我“抄袭”了。

多线程代码确实不大容易写。
《程序员》第11期101页《Java多线程编程实例》的最后一个用TreeMap实现cache的例子,按照他书上的代码可能要死锁,而他的源码则犯了和上面所说的对HashMap并发读写同样的错误 -- 对TreeMap在未同步的情况下并发写。

我也从来没有听说过Singleton的性能问题。
我们是用Java做邮件系统的,MTA都是用Java实现的,涉及到众多
多线程的程序,性能方面考虑的也不少。没有觉得Singleton会带来什么性能问题,如果说性能问题也只是说因为不合理的程序而导致的。
与Singleton没有关系。
一般我们性能优化遵循一些基本的准则:
1. 减少内存的开辟,复用提高效率。
2. 减少对象的构造,复用已经创建的对象。
3. 减少消耗时间的操作频率(采用Cache等)
4. 用基于byte[]的操作来代替字符串的操作
....

根据上面的 2 3 原则来分析下面的程序:

这样的程序,如果大部分时间耗费在calcTemperature上,那么这个程序应该用下面的程序来代替:
private Map _tempCache;

public Long getTemperature(String city) {

if(!_tempCache.contains(city)) {
Long result = calcTemperature(city);
_tempCache.put(city, result);
}
return (Long) _tempCache.get(city);
}

//替换程序
private Map _tempCache;

public Long getTemperature(String city)
{
//直接获取
Long result = _tempCache.get(city);
if (result == null) {
//如果不存在 Double Checking
synchronized(this) {
//因为内存操作比起计算或者从数据库获取快很多
result = _tempCache.get(city);
if (result == null) {
result = calcTemperature(city);
_tempCache.put(city, result);
}
}
}
return result;
}

To Readonly:
逻辑上说,没发现不能说明不存在,所以你也没有反驳到Doug Lea.
个人感觉Doug Lea说的没错,你没有产生出错值也许是因为你的电脑只有一个cpu所以你的threads其实并没有真正同时运行过。如果有两个cpu一个在读另一个在写同一个健得话感觉是有可能会出错的。没试过因为没条件。

不知道读和写不同步会不会产生什么问题?我个人在想能不能把实例对象或者是调用hashMap的put方法放到一个同步的过程中,而读取的过程不进行同步,不知道这样会不会有问题呢?下面是我的写法,还没有测试,希望大家指正。


public class CacheData {
public static java.util.Map cache;

public synchronized static void initCache() {
CacheData.cache = new java.util.HashMap();
}
public synchronized static String putCacheData(String key,String value){
CacheData.cache.put(key,value);
return value;
}
public static String getCacheDataByKey(String key, String defValue) {
if (cache == null) {
CacheData.initCache();
}
if (!cache.containsKey(key)){
return putCacheData(key,defValue);
}
return (String)cache.get(key);
}

public static void main(String[] args) {
CacheData cachedata = new CacheData();
}
}

老兄也太搞笑了,你是不是以为Doug在做广告啊?要想模拟出来你应该多找几种不同架构的机器试试,在你的机子上估计这辈子都没希望看到你期望的结果了

不好意思头回发帖,
上一个 to ReadOnly

Page 19, line 7 (excluding code)

/*
* Project : JAction
*
* Package : net.xini.jaction.thread
* FileName : BreakHashMap.java
*
* @auther : xini
* 2005-11-8 21:39:23
*
* Copyright : www.xinitek.com
*/
package net.xini.jaction.thread;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class BreakHashMap
{

private static final int THREAD_COUNT = 500;
private static final BooleanLock startLock = new BooleanLock();
private static final IntLock endLock = new IntLock();

public static void main(String[] args)
{
while (true)
{
System.out.print(".");
final Map map = new HashMap();

// init locks
endLock.count = THREAD_COUNT;
startLock.locked = true;

// construct THREAD_COUNT threads
for (int i = 0; i < THREAD_COUNT; i++)
{
new Thread()
{
public void run()
{
//System.out.print("#");
// wait for main thread to kick off
synchronized (startLock)
{
try
{
while (startLock.locked)
{
startLock.wait();
}
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}
}

// put the same key value pair
map.put("key1", "value1");
//如果象下面这样,再put一个,结果就是正确的了,为什么呢?
map.put("key2", "value2");
//map.put("key3", "value3");

synchronized (endLock)
{
endLock.count--;
if (endLock.count == 0)
{
// notify main thread
endLock.notify();
}
}
}
}.start();
}

// kick off
synchronized (startLock)
{
startLock.locked = false;
startLock.notifyAll();
}

// wait for all other thread to end
try
{
synchronized (endLock)
{
while (endLock.count > 0)
{
endLock.wait();
}
}
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}

// check map size
synchronized (map)
{
if (map.size() > 1)
{
System.out.println("\nsize: " + map.size());
Iterator iterator = map.keySet().iterator();
while (iterator.hasNext())
{
String strKey = (String)iterator.next();
System.out.println(map.get(strKey));
}
break;
}
}

// failed, continue
}
}

}

class BooleanLock
{
boolean locked;
}

class IntLock
{
int count;
}

现在有时间并且爱钻牛角尖的人还真多。