论坛首页 Java企业应用论坛

Map 四种同步方式的性能比较

浏览 23671 次
精华帖 (3) :: 良好帖 (18) :: 新手帖 (11) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-02-27  
cammette 写道
jsyx 写道
cammette 写道
jsyx 写道
cammette 写道
第一种这样会好点

synchronized(key)  
{  
    value = map.get(key);  



个人认为,这种写法不是线程安全的

为什么呢?
我们操作map时关注的是key对应的value。
只要我们改变某key的value能同步不就可以了吗?


你总要考虑到写的情况吧。
楼主的测试使用的是HashMap。
在java中,hashmap内部是使用数组来实现的。
无论是put还是get,首先都要根据key的hash值以及数组的长度以及一些其他的常数,计算出一个位置,然后对应get/put,会在这个位置内搜寻或者放置value。当map中存入的对象的个数到了一个临界值时(threshold),hashmap会重新初始化一个新的更长的数组,并且会重新分配所有已存的对象。
    public V get(Object key) {
        Object k = maskNull(key);
        int hash = hash(k);
        int i = indexFor(hash, table.length);
        Entry<K,V> e = table[i]; 
        while (true) {
            if (e == null)
                return null;
            if (e.hash == hash && eq(k, e.key)) 
                return e.value;
            e = e.next;
        }
    }

    public V put(K key, V value) {
	K k = maskNull(key);
        int hash = hash(k);
        int i = indexFor(hash, table.length);

        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            if (e.hash == hash && eq(k, e.key)) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, k, value, i);
        return null;
    }


举例来说:

如果你只同步key,那么当执行到这一行,以后
int i = indexFor(hash, table.length);

另外一个线程执行了一个新的put操作,导致数组被resize,对象被重新放置。
这样的结果就是你的get操作取不出正确的value或者直接返回null

    /**
     * Rehashes the contents of this map into a new array with a
     * larger capacity.  This method is called automatically when the
     * number of keys in this map reaches its threshold.
     *
     * If current capacity is MAXIMUM_CAPACITY, this method does not
     * resize the map, but sets threshold to Integer.MAX_VALUE.
     * This has the effect of preventing future calls.
     *
     * @param newCapacity the new capacity, MUST be a power of two;
     *        must be greater than current capacity unless current
     *        capacity is MAXIMUM_CAPACITY (in which case value
     *        is irrelevant).
     */
    void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable);
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);
    }

受教了。
不过lz的
synchronized(anObject)  
{  
    value = map.get(key);  
}  也会遇到这个问题


synchronized(anObject)  
{  
    value = map.get(key);  
}

synchronized(anObject)  
{  
    map.put(key, value);  
}
这样,我觉得不会有问题,只要保证put和get都同步到这个anObject上来

synchronized(key)  
{  
    value = map.get(key);  


synchronized(key)  
{  
    map.put(key, value); 

这样会有问题,因为put和get的key可能是不同的对象
2 请登录后投票
   发表时间:2008-03-05  
上面的评论相当的精彩

我请教一下为什么不是
synchronized(map){
  value = map.get(key);
}
0 请登录后投票
   发表时间:2008-03-08  
jomper 写道
上面的评论相当的精彩

我请教一下为什么不是
synchronized(map){
  value = map.get(key);
}

其实是一样的,
synchronized(anObject)
{
value = map.get(key);
}

synchronized(map){  
  value = map.get(key);  
}

如果anObject只用在map的get和put的同步中,效果是一样的;
0 请登录后投票
   发表时间:2008-04-22  
看楼主的图很吓人,synchronized效率这么差啊,jdk1.4的也只能用这个了吧


网上有人建议是说建一个
final static Object lockForMap=new Object();
然后
synchronized(lockForMap){
  value=map.get(key);
}

因为synchronized(map)的话,有可能map会被重新实例化,这样子也不保险。
0 请登录后投票
   发表时间:2009-02-18  
jdk1.4可以使用 EDU.oswego.cs.dl.util.concurrent下的
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics