ConcurrentMap与HashTable并非线程安全的?

小乞丐 发布于 2013/09/05 23:36
阅读 2K+
收藏 0
package cn.test;

import java.util.Hashtable;
import java.util.Map;

public class CacheDemoConcurrentHashMap {

// private  Map<String, Object> cache = new ConcurrentHashMap<String, Object>();
	private  Map<String, Object> cache = new Hashtable<String, Object>();
	public static void main(String[] args) {
		final CacheDemoConcurrentHashMap support = new CacheDemoConcurrentHashMap();  
		for(int j=0;j<10;j++){ //写入
			final String key= "key"+j;
			new Thread(new Runnable() {
				public void run() {
					support.put(key,key);
				}
			}).start();
			new Thread(new Runnable() { //读取
				public void run() {
					support.getData(key);
				}
			}).start();
		}
	}
	//取
	public  Object getData(String key){
		System.out.println("开始读取缓存数据....."+Thread.currentThread().getName());
		Object value = null;
		try{
			value = cache.get(key);
			if(value == null){
					if(value==null){
						value = "no";//实际失去queryDB();
					}
			}
			System.out.println(Thread.currentThread().getName()+"-- 读 : {key:"+key+",value: "+value+"}");
		}finally{
			System.out.println("结束读取缓存数据....."+Thread.currentThread().getName());
		}
		return value;
	}
	//存
	public void put(String key,Object value){
		System.out.println("开始写入缓存数据....."+Thread.currentThread().getName());
		try{
			Thread.sleep(1000);
			if(key != null){
				cache.put(key, value);
			}
			System.out.println(Thread.currentThread().getName()+"-- 写 :  {key:"+key+",value: "+value+"}");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally{
			System.out.println("结束写入缓存数据....."+Thread.currentThread().getName());
		}
		
	}
}


得到结果:
 开始写入缓存数据.....Thread-0
开始读取缓存数据.....Thread-1
Thread-1-- 读 : {key:key0,value: no}
结束读取缓存数据.....Thread-1
开始写入缓存数据.....Thread-4
开始读取缓存数据.....Thread-5
Thread-5-- 读 : {key:key2,value: no}
结束读取缓存数据.....Thread-5
开始写入缓存数据.....Thread-2
开始读取缓存数据.....Thread-3
Thread-3-- 读 : {key:key1,value: no}
结束读取缓存数据.....Thread-3
开始写入缓存数据.....Thread-6
开始读取缓存数据.....Thread-7
Thread-7-- 读 : {key:key3,value: no}
开始读取缓存数据.....Thread-9
Thread-9-- 读 : {key:key4,value: no}
结束读取缓存数据.....Thread-9
结束读取缓存数据.....Thread-7
开始读取缓存数据.....Thread-13
开始写入缓存数据.....Thread-8
开始读取缓存数据.....Thread-17
开始写入缓存数据.....Thread-12
开始写入缓存数据.....Thread-16
Thread-13-- 读 : {key:key6,value: no}
Thread-17-- 读 : {key:key8,value: no}
结束读取缓存数据.....Thread-13
结束读取缓存数据.....Thread-17
开始写入缓存数据.....Thread-10
开始读取缓存数据.....Thread-11
Thread-11-- 读 : {key:key5,value: no}
结束读取缓存数据.....Thread-11
开始写入缓存数据.....Thread-14
开始读取缓存数据.....Thread-15
Thread-15-- 读 : {key:key7,value: no}
结束读取缓存数据.....Thread-15
开始写入缓存数据.....Thread-18
开始读取缓存数据.....Thread-19
Thread-19-- 读 : {key:key9,value: no}
结束读取缓存数据.....Thread-19
Thread-4-- 写 :  {key:key2,value: key2}
Thread-0-- 写 :  {key:key0,value: key0}
结束写入缓存数据.....Thread-4
结束写入缓存数据.....Thread-0
Thread-2-- 写 :  {key:key1,value: key1}
结束写入缓存数据.....Thread-2
Thread-6-- 写 :  {key:key3,value: key3}
结束写入缓存数据.....Thread-6
Thread-8-- 写 :  {key:key4,value: key4}
Thread-12-- 写 :  {key:key6,value: key6}
Thread-10-- 写 :  {key:key5,value: key5}
Thread-16-- 写 :  {key:key8,value: key8}
结束写入缓存数据.....Thread-8
结束写入缓存数据.....Thread-12
结束写入缓存数据.....Thread-10
结束写入缓存数据.....Thread-16
Thread-14-- 写 :  {key:key7,value: key7}
结束写入缓存数据.....Thread-14
Thread-18-- 写 :  {key:key9,value: key9}
结束写入缓存数据.....Thread-18

当cache为ConcurrentMap的时候:

开始写入缓存数据.....Thread-0
开始读取缓存数据.....Thread-1
开始写入缓存数据.....Thread-2
Thread-1-- 读 : {key:key0,value: no}
开始读取缓存数据.....Thread-3
结束读取缓存数据.....Thread-1
Thread-3-- 读 : {key:key1,value: no}
结束读取缓存数据.....Thread-3
开始写入缓存数据.....Thread-4
开始写入缓存数据.....Thread-6
开始读取缓存数据.....Thread-5
Thread-5-- 读 : {key:key2,value: no}
结束读取缓存数据.....Thread-5
开始写入缓存数据.....Thread-8
开始读取缓存数据.....Thread-9
Thread-9-- 读 : {key:key4,value: no}
开始写入缓存数据.....Thread-10
结束读取缓存数据.....Thread-9
开始写入缓存数据.....Thread-12
开始写入缓存数据.....Thread-14
开始写入缓存数据.....Thread-16
开始读取缓存数据.....Thread-13
开始写入缓存数据.....Thread-18
Thread-13-- 读 : {key:key6,value: no}
开始读取缓存数据.....Thread-15
开始读取缓存数据.....Thread-11
结束读取缓存数据.....Thread-13
开始读取缓存数据.....Thread-17
开始读取缓存数据.....Thread-7
Thread-15-- 读 : {key:key7,value: no}
Thread-11-- 读 : {key:key5,value: no}
开始读取缓存数据.....Thread-19
Thread-17-- 读 : {key:key8,value: no}
Thread-7-- 读 : {key:key3,value: no}
结束读取缓存数据.....Thread-15
结束读取缓存数据.....Thread-11
Thread-19-- 读 : {key:key9,value: no}
结束读取缓存数据.....Thread-17
结束读取缓存数据.....Thread-7
结束读取缓存数据.....Thread-19
Thread-0-- 写 :  {key:key0,value: key0}
结束写入缓存数据.....Thread-0
Thread-4-- 写 :  {key:key2,value: key2}
结束写入缓存数据.....Thread-4
Thread-8-- 写 :  {key:key4,value: key4}
结束写入缓存数据.....Thread-8
Thread-12-- 写 :  {key:key6,value: key6}
结束写入缓存数据.....Thread-12
Thread-16-- 写 :  {key:key8,value: key8}
结束写入缓存数据.....Thread-16
Thread-2-- 写 :  {key:key1,value: key1}
Thread-10-- 写 :  {key:key5,value: key5}
Thread-14-- 写 :  {key:key7,value: key7}
Thread-18-- 写 :  {key:key9,value: key9}
结束写入缓存数据.....Thread-2
Thread-6-- 写 :  {key:key3,value: key3}
结束写入缓存数据.....Thread-10
结束写入缓存数据.....Thread-14
结束写入缓存数据.....Thread-18
结束写入缓存数据.....Thread-6

这说明:
    当有A线程在对concurrentmap或者hashtable写入数据的时候,线程B还能对其进行读取数据。
难道 concurrentmap 并 没 有将所谓整个桶锁住 和 hashtable将整个hash表锁住?
但是在源码中,concurrentmap的put方法是对当前线程加了锁的呀?

        V put(K key, int hash, V value, boolean onlyIfAbsent) {
            lock();
            try {
                int c = count;
                if (c++ > threshold) // ensure capacity
                    rehash();
                HashEntry[] tab = table;
                int index = hash & (tab.length - 1);
                HashEntry<K,V> first = (HashEntry<K,V>) tab[index];
                HashEntry<K,V> e = first;
                while (e != null && (e.hash != hash || !key.equals(e.key)))
                    e = e.next;

                V oldValue;
                if (e != null) {
                    oldValue = e.value;
                    if (!onlyIfAbsent)
                        e.value = value;
                }
                else {
                    oldValue = null;
                    ++modCount;
                    tab[index] = new HashEntry<K,V>(key, hash, first, value);
                    count = c; // write-volatile
                }
                return oldValue;
            } finally {
                unlock();
            }
        }
hashtable也加了synchronized关键字的
 public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) {
        throw new NullPointerException();
    }

    // Makes sure the key is not already in the hashtable.
    Entry tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {
        V old = e.value;
        e.value = value;
        return old;
        }
    }

    modCount++;
    if (count >= threshold) {
        // Rehash the table if the threshold is exceeded
        rehash();

            tab = table;
            index = (hash & 0x7FFFFFFF) % tab.length;
    } 

    // Creates the new entry.
    Entry<K,V> e = tab[index];
    tab[index] = new Entry<K,V>(hash, key, value, e);
    count++;
    return null;
    }

凌乱了~~~
加载中
1
mallon
mallon
查一下源代码不就是了
0
小乞丐
小乞丐

引用来自“Mallon”的答案

查一下源代码不就是了

看过了,concurrentmap put方法内部加了lock,hashtable put方法上加了synchronized。

难道说明他们内部是线程是安全的,是说明在多线程并发的情况下,线程并不是同步的。

但是这样他们的优越性体现在哪里呢?就算是hashmap一个线程的put的时候,另一个线程又在什么场景下才能修改掉正在put的值呢?

0
王正航
王正航
兄弟是你理解错了,Hashtable的方法是线程安全的,指的是操作Hashtable方法时是线程安全的,而你的例子里面显然已经要把同步的范围扩大了,CacheDemoConcurrentHashMap.getData和CacheDemoConcurrentHashMap.put是需要线程安全的虽然它们里面调用了Hashtable.get和Hashtable.put但除了这两语调用还有很多其他的语句,它们不是线程安全的,比如:
if(value == null){
					if(value==null){
						value = "no";//实际失去queryDB();
					}
			}
这里面的value是很多线程都能操作到的!

另外ConcurrentHashMap它的线程安全是跟Hashtable又不一样,它并没有锁定整个表,所以你在遍历种个ConcurrentHashMap的时候同时又增加或删除了里面的数据是不会报ConcurrentModificationException异常的。
0
穿山乙
穿山乙

引用来自“王正航”的评论

兄弟是你理解错了,Hashtable的方法是线程安全的,指的是操作Hashtable方法时是线程安全的,而你的例子里面显然已经要把同步的范围扩大了,CacheDemoConcurrentHashMap.getData和CacheDemoConcurrentHashMap.put是需要线程安全的虽然它们里面调用了Hashtable.get和Hashtable.put但除了这两语调用还有很多其他的语句,它们不是线程安全的,比如:
if(value == null){
					if(value==null){
						value = "no";//实际失去queryDB();
					}
			}
这里面的value是很多线程都能操作到的!

另外ConcurrentHashMap它的线程安全是跟Hashtable又不一样,它并没有锁定整个表,所以你在遍历种个ConcurrentHashMap的时候同时又增加或删除了里面的数据是不会报ConcurrentModificationException异常的。
正解,你存和取的方法没有加锁,存在线程不安全,因此会出现读线程没读完,写线程又在写的情况,线程安全只存在于hashtable或concurrentMap的put和get这一个动作,其他动作不能保证。
0
尚浩宇
尚浩宇
单步操作是安全的,但你的逻辑执行时不是原子的,
返回顶部
顶部