Map容器线程安全问题

一、HashMap在非线程安全的环境下使用会出现什么样的问题?

public class HashMapMultiThread {  
    static Map<String,String> map = new HashMap<String,String>();  
    public static class AddThread implements Runnable{  
        int start = 0 ;  
        public AddThread(int start){  
            this.start = start;  
        }  
        @Override    
        public void run() {  
            for (int i = 0; i < 100000; i+=2) {  
                map.put(Integer.toString(i), Integer.toBinaryString(i));  
                  
            }  
        }  
    }  
      
    public static void main(String[] args) throws InterruptedException {  
          
        Thread t1 = new Thread(new HashMapMultiThread.AddThread(0));  
        Thread t2 = new Thread(new HashMapMultiThread.AddThread(1));  
        t1.start();  
        t2.start();  
        t1.join();  
        t2.join();  
        System.out.println(map.size());  
    }  
}  

 

上述代码使用t1和t2两个线程同时对HashMap进行put()操作,如果一切正常,我们期望得到的map.size()就是100000.但实际上,你可能会得到以下三种情况(注意,这里使用JDK7进行试验): 

第一:程序正常结束,并且结果也是符合预期的。HashMap的大小为100000.

第二:程序正常结束,但结果不符合预期,而是一个小于100000的数字,比如98868.

第三:程序永远无法结束。并发形成循环链表,导致死循环。

二、ConcurrentHashMap不能解决所有线程安全问题

对于ConcurrentHashMap,如果只调用get或put方法是线程安全的,但你调用get后再调用put之前,如果有另一个线程也调用了put就很可能把前面的操作结果覆盖了,因为违反了原则操作的规则。此时可用putIfAbsent方法代替。如下面的例子

public class ConcurrentHashMapTest {

    private static final ConcurrentMap<String, AtomicInteger> CACHE_MAP = new ConcurrentHashMap<>();
    private static final String KEY = "test";

    private static class TestThread implements Runnable{
        @Override
        public void run() {
            if(CACHE_MAP.get(KEY)==null){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                CACHE_MAP.put(KEY, new AtomicInteger());
            }
            CACHE_MAP.get(KEY).incrementAndGet();
        }
    }

    public static void main(String[] args) {
        new Thread(new TestThread()).start();
        new Thread(new TestThread()).start();
        new Thread(new TestThread()).start();
        try {
            Thread.sleep(800);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("次数:"+CACHE_MAP.get(KEY).get());
    }
}

 

posted on 2018-05-22 23:43  时间朋友  阅读(387)  评论(0编辑  收藏  举报

导航