Java多线程工具包java.util.concurrent---ConcurrentHashMap

Posted yvan1115

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java多线程工具包java.util.concurrent---ConcurrentHashMap相关的知识,希望对你有一定的参考价值。

参考以下博客
http://blog.csdn.net/xiaohui127/article/details/11928865
http://blog.csdn.net/xuefeng0707/article/details/40797085
http://www.importnew.com/21388.html
本文没有具体探讨ConcurrentHashMap、HashMap、HashTable的实现,只是对其做了比较和总结,在工作中该如何使用,避免不必要的错误


什么是ConcurrentHashMap

ConcurrentHashMap 和 java.util.HashTable 类很相似,但 ConcurrentHashMap 能够提供比 HashTable 更好的并发性能。在你从中读取对象的时候 ConcurrentHashMap 并不会把整个 Map 锁住。此外,在你向其中写入对象的时候,ConcurrentHashMap 也不会锁住整个 Map。它的内部只是把 Map 中正在被写入的部分进行锁定。
另外一个不同点是,在被遍历的时候,即使是 ConcurrentHashMap 被改动,它也不会抛 ConcurrentModificationException。

有什么特点

1、允许并发的读和线程安全的更新操作
2、在执行写操作时,只锁住部分的Map

  • 这里说明以下同为线程安全的HashTable,主要区别在于
    ConcurrentHashMap是分段锁(Segmentation),而HashTable是锁住整个map,在并发性能上ConcurrentHashMap优于HashTable

3、并发的更新是通过内部根据并发级别将Map分割成小部分实现的
4、高的并发级别会造成时间和空间的浪费,低的并发级别在写线程多时会引起线程间的竞争
5、所有操作都是线程安全
6、返回的迭代器是弱一致性,fail-safe并且不会抛出ConcurrentModificationException异常
7、不允许null的键值

  • key和value都不允许为null

8、可以使用ConcurrentHashMap代替HashTable,但要记住ConcurrentHashMap不会锁住整个Map

HashMap造成cpu100%的问题

Java HashMap的死循环
这篇文章详细介绍了多线程下HashMap在rehash过程中造成死循环,引起cpu100%的过程和原因。
简单来说:由于rehash的机制,多线程下使hashmap内部形成了环形链表.

示例

这里简单写一个利用线程安全的特点ConcurrentHashMap作为数据缓存。
ConcurrentHashMap比较适合多读少写的场景

class AddressData 
    private static ConcurrentMap<String, Object> map = new ConcurrentHashMap<>();

    public static Object getData(String param) 
        Object result = null;
        try 
            result = map.get(param);
            if (result == null) 
                HttpHelper helper = new HttpHelper();
                // 如果key已经存在,那么返回的值就是key对应的value
                // 如果key不存在,那么返回的值为null
                map.putIfAbsent(param, helper.getProviceCityArea());
                result = map.get(param);
            

         catch (Exception e) 
            // TODO 异常管理
        
        return result;
    


class HttpHelper 

    public Address getProviceCityArea() 
        Address address = new Address();
        address.setProviceCode("1");
        address.setProviceCode("四川省");
        List<City> cites = new ArrayList<>();
        City city = new City();
        city.setCityCode("10");
        city.setCityName("成都市");
        List<Area> areas = new ArrayList<>();
        Area area = new Area();
        area.setAreaCode("101");
        area.setAreaName("锦江区");
        areas.add(area);
        city.setAreas(areas);
        cites.add(city);
        address.setCites(cites);
        return address;
    


class Address 
    private String proviceCode;
    private String proviceName;
    private List<City> cites;

    public String getProviceCode() 
        return proviceCode;
    

    public void setProviceCode(String proviceCode) 
        this.proviceCode = proviceCode;
    

    public String getProviceName() 
        return proviceName;
    

    public void setProviceName(String proviceName) 
        this.proviceName = proviceName;
    

    public List<City> getCites() 
        return cites;
    

    public void setCites(List<City> cites) 
        this.cites = cites;
    

    @Override
    public String toString() 
        return "Address [proviceCode=" + proviceCode + ", proviceName=" + proviceName + ", cites=" + cites.toString()
                + "]";
    



class City 
    private String cityCode;
    private String cityName;
    private List<Area> areas;

    public String getCityCode() 
        return cityCode;
    

    public void setCityCode(String cityCode) 
        this.cityCode = cityCode;
    

    public String getCityName() 
        return cityName;
    

    public void setCityName(String cityName) 
        this.cityName = cityName;
    

    public List<Area> getAreas() 
        return areas;
    

    public void setAreas(List<Area> areas) 
        this.areas = areas;
    

    @Override
    public String toString() 
        return "City [cityCode=" + cityCode + ", cityName=" + cityName + ", areas=" + areas.toString() + "]";
    



class Area 
    private String areaCode;
    private String areaName;

    public String getAreaCode() 
        return areaCode;
    

    public void setAreaCode(String areaCode) 
        this.areaCode = areaCode;
    

    public String getAreaName() 
        return areaName;
    

    public void setAreaName(String areaName) 
        this.areaName = areaName;
    

    @Override
    public String toString() 
        return "Area [areaCode=" + areaCode + ", areaName=" + areaName + "]";
    

探讨示例

前面提到的concurrentHashMap线程安全,主要是利用了分段锁来实现,保证在每次访问只允许一个线程修改哈希表的映射关系
但是是否具有原子性?
ConcurrentMap能够保证每一次调用(例如一次putIfAbsent)都是原子操作,不受多线程影响,但并不保证多次调用之间也是原子操作

package com.yvan.concurrentMap;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
/**
 * 
 * @author yvan
 *
 */
public class AppMain 

    private static ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<Integer, Integer>(); 
    public static void main(String[] args) throws InterruptedException   
        CountDownLatch countDownLatch = new CountDownLatch(3);
        map.put(3, 1);
        new Thread("Thread1")  
            @Override  
            public void run()   
                System.out.println(Thread.currentThread().getName()+"s=="+map.get(3));
                int temp = map.get(3)+1;
                map.put(3, temp);
                System.out.println(Thread.currentThread().getName()+"e=="+map.get(3));
                countDownLatch.countDown();
              
        .start();  

        new Thread("Thread2")  
            @Override  
            public void run()   
                System.out.println(Thread.currentThread().getName()+"s=="+map.get(3));
                int temp = map.get(3)+1;
                map.put(3, temp);
                System.out.println(Thread.currentThread().getName()+"e=="+map.get(3));
                countDownLatch.countDown();
              
        .start();  

        new Thread("Thread3")  
            @Override  
            public void run()   
                System.out.println(Thread.currentThread().getName()+"s=="+map.get(3));
                int temp = map.get(3)+1;
                map.put(3, temp);
                System.out.println(Thread.currentThread().getName()+"e=="+map.get(3));
                countDownLatch.countDown();
              
        .start();  
        countDownLatch.await();
        System.out.println(map.get(3));

      

结果

Thread1s==1
Thread1e==2
Thread2s==1
Thread2e==3
Thread3s==3
Thread3e==4
4

以上是关于Java多线程工具包java.util.concurrent---ConcurrentHashMap的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程工具包java.util.concurrent---CyclicBarrier

Java多线程工具包java.util.concurrent---ExecutorService

Java多线程工具包java.util.concurrent---ReadWriteLock

Java多线程工具包java.util.concurrent---Lock

Java多线程_同步工具CyclicBarrier

Java多线程(线程池原子性并发工具类)