线程不安全的HashMap

因为多线程环境下,使用Hashmap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap。

效率低下的HashTable容器

HashTable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下HashTable的效率非常低下。

因为当一个线程访问HashTable的同步方法时,其他线程访问HashTable的同步方法时,可能会进入阻塞或轮询状态。

如线程1使用put进行添加元素,线程2不但不能使用put方法添加元素,并且也不能使用get方法来获取元素,所以竞争越激烈效率越低。

ConcurrentHashMap的锁分段技术

HashTable容器在竞争激烈的并发环境下表现出效率低下的原因,是因为所有访问HashTable的线程都必须竞争同一把锁。

那假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术。

首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。

Segment是一种重入锁ReentrantLock,在ConcurrentHashMap里扮演锁的角色,

HashEntry则用于存储键值对数据。

一个ConcurrentHashMap里包含一个Segment数组,  每个Segment守护者一个HashEntry数组里的元素,

当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁。

 

ConcurrentHashMap使用方法:

虽然ConcurrentHashMap是线程安全的,如果你只调用get(),或只调用put()时,ConcurrentHashMap是线程安全的。

但是,在你调用完get后,调用put之前,如果有另外一个线程调用了put(name, x),你再去执行put(name,x),就很可能把前面的操作结果覆盖掉了。

所以,即使在线程安全的情况下,你还是有可能违反原子操作的规则。

当然可以用 锁 解决这个问题,但性能受到很大影响。

另一种思路,也可以使用ConcurrentMap定义的循环CAS方法:

    1. putIfAbsent(K key, V value)
    2. 如果key对应的value不存在,则put进去,返回null。否则不put,返回已存在的value
    3. boolean remove(Object key, Object value)
    4. 如果key对应的值是value,则移除K-V,返回true。否则不移除,返回false。
    5. boolean replace(K key, V oldValue, V newValue)  //循环CAS算法
    6. 如果key对应的当前值是oldValue,则替换为newValue,返回true。否则不替换,返回false。
  1. public static void demo1() {
  2. final Map<String, Integer> count = new ConcurrentHashMap<>();
  3. Runnable task = new Runnable() {
  4. @Override
  5. public void run() {
  6. Integer oldValue, newValue;
  7. for (int i = 0; i < 5; i++) {
  8. while (true) {
  9. oldValue = count.get("a");
  10. if (null == oldValue) {
  11. newValue = 1;
  12. if (count.putIfAbsent("a", newValue) == null) {
  13. break;
  14. }
  15. } else {
  16. newValue = oldValue + 1;
  17. if (count.replace("a", oldValue, newValue)) {
  18. break;
  19. }
  20. }
  21. }
  22. }
  23. }
  24. };
  25. new Thread(task).start();
  26. new Thread(task).start();
  27. }

还一种思路,也可以使用Atomic原子操作方法(本质也是循环CAS):

    1. public static void demo1() {
    2. final Map<String, AtomicInteger> count = new ConcurrentHashMap<>();
    3. Runnable task = new Runnable() {
    4. @Override
    5. public void run() {
    6. AtomicInteger oldValue;
    7. for (int i = 0; i < 5; i++) {
    8. oldValue = count.get("a");
    9. if (null == oldValue) {
    10. AtomicInteger zeroValue = new AtomicInteger(0);
    11. oldValue = count.putIfAbsent("a", zeroValue);  //赋予初始值:0
    12. if (null == oldValue) {
    13. oldValue = zeroValue;
    14. }
    15. }
    16. oldValue.incrementAndGet();  //原子循环CAS自增操作
    17. }
    18. }
    19. };
    20. new Thread(task).start();
    21. new Thread(task).start();

最新文章

  1. java中文乱码解决之道(一)-----认识字符集
  2. 响应式WEB设计的9项基本原则
  3. 用redux完成事务清单
  4. three.js 场景入门
  5. JSON的操作
  6. Codeforces 235E
  7. C++ 顺序容器 vector list deque 之比较
  8. 201521123084 《Java程序设计》第12周学习总结
  9. jQuery中foreach的continue和break
  10. python笔记:#008#变量的命名
  11. mobile_竖向滑屏
  12. Linux sleep 语句以及循环 测试负载
  13. elasticsearch best_fields most_fields cross_fields从内在实现看区别——本质就是前两者是以field为中心,后者是词条为中心
  14. Asp .Net core 2 学习笔记(2) —— 中间件
  15. mysql多表查询,group by并将结果导出来csv文件
  16. bzoj3196: Tyvj 1730 二逼平衡树 树套树
  17. Python学习记录day8
  18. Kubernetes Deployment与Replica Set
  19. 编程之美 set 5 寻找数组中最大值和最小值
  20. 2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛 H. Skiing (拓扑排序+假dp)

热门文章

  1. php的流程控制 if elseif swich case for循环
  2. Http的通信机制?
  3. API网络数据安全
  4. oracle存储过程及sql优化-(一)
  5. sql server关键字大全
  6. LeetCode 17. 电话号码的字母组合(Letter Combinations of a Phone Number)
  7. spark 笔记 12: Executor,task最后的归宿
  8. 方法三破解:Excel工作表保护密码
  9. Windows下搭建Docker与Kubernetes(DevOps一)
  10. java连接oracle并load sql从xml执行查询