概述

上次记录了关于ConCurrentHashMap的原理以及源码,现在我对其进行有关功能的测试,下面就讲解一下我测试的内容和代码。这些测试主要针对JDK1.7版本。

GET安全测试

上一篇写过get方法是没有加锁的,因为HashEntry的value和next属性是volatile的,volatile直接保证了可见性,所以读的时候可以不加锁,现在写一个程序,启动20个线程,只有一个key="count",每个线程都要执行 map.put(key, 1)  或者 map.put(key, value + 1) ,所以理论上说,20个线程会得到值("count",20),所有的源码如下:

  1. 1 package test;
  2. 2
  3. 3 import java.io.Serializable;
  4. 4 import java.util.concurrent.ConcurrentHashMap;
  5. 5 import java.util.concurrent.ExecutorService;
  6. 6 import java.util.concurrent.Executors;
  7. 7 import java.util.concurrent.locks.ReentrantLock;
  8. 8
  9. 9 /**
  10. 10 * Created by Administrator on 2019/12/4.
  11. 11 */
  12. 12 public class TestConcurrentHashMap {
  13. 13
  14. 14
  15. 15 public static void main(String[] args) {
  16. 16 DoWork dw = new DoWork(map);
  17. 17 //map.put("1",1);
  18. 18 ExecutorService pool = Executors.newFixedThreadPool(8);
  19. 19 try {
  20. 20 for (int i = 0; i < 20; i++) {
  21. 21 pool.execute(new Thread(dw));// 开启20个线程
  22. 22 }
  23. 23 Thread.sleep(5000);// 主线程睡眠5s 等待子线程完成任务
  24. 24 } catch (Exception e) {
  25. 25 e.printStackTrace();
  26. 26 } finally {
  27. 27 pool.shutdown();// 关闭线程池
  28. 28 }
  29. 29 System.out.println("统计的数量:" + map.get("count"));
  30. 30
  31. 31 }
  32. 32 private static ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();
  33. 33
  34. 34 static class DoWork extends ReentrantLock implements Serializable,Runnable {
  35. 35
  36. 36 private ConcurrentHashMap<String, Integer> map = null;
  37. 37
  38. 38 public DoWork(ConcurrentHashMap<String, Integer> map) {
  39. 39 this.map = map;
  40. 40 }
  41. 41
  42. 42 @Override
  43. 43 public void run() {
  44. 44 add("count");
  45. 45 }
  46. 46
  47. 47 public void add(String key) {
  48. 48 Integer value = map.get(key);// 获取map中的数值
  49. 49 System.out.println("当前数量" + value);
  50. 50 if (null == value) {
  51. 51 map.put(key, 1);// 第一次存放
  52. 52 } else {
  53. 53 map.put(key, value + 1);// 以后次存放
  54. 54 }
  55. 55 }
  56. 56 public void addLock(String key) {
  57. 57 lock();
  58. 58 try {
  59. 59 Integer value = map.get(key);
  60. 60 System.out.println("当前数量" + value);
  61. 61 if (null == value) {
  62. 62 map.put(key, 1);
  63. 63 } else {
  64. 64 map.put(key, value + 1);
  65. 65 }
  66. 66 } finally {
  67. 67 unlock();
  68. 68 }
  69. 69 }
  70. 70
  71. 71
  72. 72
  73. 73 }
  74. 74
  75. 75 }

在如上测试代码中有如下问题:

问题1:为什么开启20个线程,启动的线程池确是8个 见第18行 21行代码?

回答:这样可以更大的概率实现线程并发。也就是更容易出现问题。

问题2:为什么要睡眠5s,

回答:那是因为统计结果的时候尽量确认所有线程执行完了,结果更加准确。

问题3:ReentrantLock 方法是干嘛的?

回答:ReentrantLock实现了独占功能,是这里使用的原因。

当在线程run方法中执行add(String key)方法时候,执行结果如下:

明显有问题的说,put是有锁的,所以先猜测get存在不安全的嫌疑,现在查看get方法源码,说明get确实没有加锁。所以说明的问题是虽然put有原子性,但是get+put就没有原子性,也就是说,当第一个线程get一个值之后,还没有put进去的时候,就被第二个线程get了,所以第二个线程get的值就是第一个线程put之前的值。

  1. 1 public V get(Object key) {
  2. 2 Segment<K,V> s; // manually integrate access methods to reduce overhead
  3. 3 HashEntry<K,V>[] tab;
  4. 4 int h = hash(key);
  5. 5 long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
  6. 6 if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
  7. 7 (tab = s.table) != null) {
  8. 8 for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
  9. 9 (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
  10. 10 e != null; e = e.next) {
  11. 11 K k;
  12. 12 if ((k = e.key) == key || (e.hash == h && key.equals(k)))
  13. 13 return e.value;
  14. 14 }
  15. 15 }
  16. 16 return null;
  17. 17 }

现在对add方法执行加锁方法,执行测试代码的56行代码的addLock方法。

然后debug输出是:

并发效率测试

现在模拟1000个并发,每个测试1000次操作,循环测试10轮。分别测试Put和Get操作,测试对象HashMapSync、ConcurrentHashMap、Hashtable。源码如下:

  1. 1 package test;
  2. 2 import java.util.Collections;
  3. 3 import java.util.HashMap;
  4. 4 import java.util.Hashtable;
  5. 5 import java.util.Map;
  6. 6 import java.util.concurrent.ConcurrentHashMap;
  7. 7
  8. 8 import static javafx.scene.input.KeyCode.T;
  9. 9
  10. 10
  11. 11 /**
  12. 12 * 测试HashMap和ConcurrentHashMap的并发性能差别。
  13. 13 *
  14. 14 *
  15. 15 */
  16. 16 public class TestConCurrent {
  17. 17 static final int threads = 1000;
  18. 18 static final int NUMBER = 1000;
  19. 19
  20. 20 public static void main(String[] args) throws Exception {
  21. 21 Map<String, Integer> hashmapSync = Collections
  22. 22 .synchronizedMap(new HashMap<String, Integer>());
  23. 23 Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<String, Integer>();
  24. 24 Map<String, Integer> hashtable = new Hashtable<String, Integer>();
  25. 25 long totalA = 0;
  26. 26 long totalB = 0;
  27. 27 long totalC = 0;
  28. 28 for (int i = 0; i <= 10; i++) {
  29. 29 totalA += testPut(hashmapSync);
  30. 30 totalB += testPut(concurrentHashMap);
  31. 31 totalC += testPut(hashtable);
  32. 32 }
  33. 33 System.out.println("Put time HashMapSync=" + totalA + "ms.");
  34. 34 System.out.println("Put time ConcurrentHashMap=" + totalB + "ms.");
  35. 35 System.out.println("Put time Hashtable=" + totalC + "ms.");
  36. 36 totalA = 0;
  37. 37 totalB = 0;
  38. 38 totalC = 0;
  39. 39 for (int i = 0; i <= 10; i++) {
  40. 40 totalA += testGet(hashmapSync);
  41. 41 totalB += testGet(concurrentHashMap);
  42. 42 totalC += testGet(hashtable);
  43. 43 }
  44. 44 System.out.println("Get time HashMapSync=" + totalA + "ms.");
  45. 45 System.out.println("Get time ConcurrentHashMap=" + totalB + "ms.");
  46. 46 System.out.println("Get time Hashtable=" + totalC + "ms.");
  47. 47 }
  48. 48 public static long testPut(Map<String, Integer> map) throws Exception {
  49. 49 long start = System.currentTimeMillis();
  50. 50 for (int i = 0; i < threads; i++) {
  51. 51 new MapPutThread(map).start();
  52. 52 }
  53. 53 while (MapPutThread.counter > 0) {
  54. 54 Thread.sleep(1);
  55. 55 }
  56. 56 return System.currentTimeMillis() - start;
  57. 57 }
  58. 58 public static long testGet(Map<String, Integer> map) throws Exception {
  59. 59 long start = System.currentTimeMillis();
  60. 60 for (int i = 0; i < threads; i++) {
  61. 61 new MapGetThread(map).start();
  62. 62 }
  63. 63 while (MapGetThread.counter > 0) {
  64. 64 Thread.sleep(1);
  65. 65 }
  66. 66 return System.currentTimeMillis() - start;
  67. 67 }
  68. 68 }
  69. 69 class MapPutThread extends Thread {
  70. 70 static int counter = 0;
  71. 71 static Object lock = new Object();
  72. 72 private Map<String, Integer> map;
  73. 73 private String key = this.getId() + "";
  74. 74 MapPutThread(Map<String, Integer> map) {
  75. 75 synchronized (lock) {
  76. 76 counter++;
  77. 77 }
  78. 78 this.map = map;
  79. 79 }
  80. 80 public void run() {
  81. 81 for (int i = 1; i <= TestConCurrent.NUMBER; i++) {
  82. 82 map.put(key, i);
  83. 83 }
  84. 84 synchronized (lock) {
  85. 85 counter--;
  86. 86 }
  87. 87 }
  88. 88 }
  89. 89 class MapGetThread extends Thread {
  90. 90 static int counter = 0;
  91. 91 static Object lock = new Object();
  92. 92 private Map<String, Integer> map;
  93. 93 private String key = this.getId() + "";
  94. 94 MapGetThread(Map<String, Integer> map) {
  95. 95 synchronized (lock) {
  96. 96 counter++;
  97. 97 }
  98. 98 this.map = map;
  99. 99 }
  100. 100 public void run() {
  101. 101 for (int i = 1; i <= TestConCurrent.NUMBER; i++) {
  102. 102 map.get(key);
  103. 103 }
  104. 104 synchronized (lock) {
  105. 105 counter--;
  106. 106 }
  107. 107 }
  108. 108 }

如上代码只是测试效率的简单测试:

测试结果是:

总结

能动手的尽量不要猜,能用代码的尽量不要相信原理。

感谢网络大神,参考链接:

https://blog.csdn.net/java2000_net/article/details/3373181

https://www.cnblogs.com/yanphet/p/5726919.html

JAVA-JDK1.7-ConCurrentHashMap-测试和验证的更多相关文章

  1. dubbox部署到jdk1.7环境,启动:java.lang.NoSuchMethodError: java.util.concurrent.ConcurrentHashMap.keySet()

    本地用jdk1.8编译的服务提供端war包,部署到环境报错了: INFO: Initializing Spring root WebApplicationContext [16/08/17 05:14 ...

  2. Java JDK1.5、1.6、1.7新特性整理(转)

    原文链接:http://www.cnblogs.com/tony-yang-flutter/p/3503935.html 一.Java JDK1.5的新特性 1.泛型: List<String& ...

  3. Java JDK1.5、1.6、1.7新特性整理

    转载请注明出处:http://www.cnblogs.com/tony-yang-flutter 一.Java JDK1.5的新特性 1.泛型: List<String> strs = n ...

  4. linux环境下安装jdk(本文示例是jdk1.6.0_export JAVA_HOME=/usr/java/jdk1.6.0_45 export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar45)

    第一步:创建一个文件夹安装jdk(虽说地址一般自定义,但是为了方便查找请按照笔者建议目录 ):/usr/java 将jdk-6u45-linux-x64.bin文件放到   /usr/java 文件夹 ...

  5. win10 如何配置 java jdk1.8环境变量(2017.2.24)

    win10 如何配置 java jdk 环境变量 这里的win10 为全新安装的系统 一.安装 下载 jdk 64位 windows 版本安装(默认安装) 默认安装的路径: C:\Program Fi ...

  6. 安装JDK出现错误:-bash: /usr/java/jdk1.7.0_71/bin/java: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory解决办法

    1.错误描述:安装好jdk之后,通过java -version,javac,java等命令测试是否安装成功时出现错误-bash: /usr/java/jdk1.7.0_71/bin/java: /li ...

  7. 死磕 java集合之ConcurrentHashMap源码分析(三)

    本章接着上两章,链接直达: 死磕 java集合之ConcurrentHashMap源码分析(一) 死磕 java集合之ConcurrentHashMap源码分析(二) 删除元素 删除元素跟添加元素一样 ...

  8. Java工具类——通过配置XML验证Map

    Java工具类--通过配置XML验证Map 背景 在JavaWeb项目中,接收前端过来的参数时通常是使用我们的实体类进行接收的.但是呢,我们不能去决定已经搭建好的框架是怎么样的,在我接触的框架中有一种 ...

  9. .net 客户端调用java或.net webservice进行soapheader验证

    .net 客户端调用java或.net webservice进行soapheader验证 最近项目中有业务需要跨平台调用web服务,客户端和服务器之间采用非对称加密来保证数据的安全性,webservi ...

  10. Mockito:一个强大的用于Java开发的模拟测试框架

    https://blog.csdn.net/zhoudaxia/article/details/33056093 介绍 本文将介绍模拟测试框架Mockito的一些基础概念, 介绍该框架的优点,讲解应用 ...

随机推荐

  1. Linux下安装chrome

    目录 一.Centos系列 二.Ubuntu系列 一.Centos系列 1.配置yum下载源 vim /etc/yum.repos.d/chrome.repo [google-chrome] name ...

  2. 如何完成符合ISO 26262要求的基于模型设计(MBD)的测试

    背景介绍 随着汽车行业的迅速发展,汽车的复杂程度不断增加,越来越多的汽车电子控制系统具有与安全相关的功能,因此对ECU的安全要求也越来越高.复杂的软件功能,将会带来大量的软件风险问题,如何保证软件的安 ...

  3. mysql使用自定义序列实现row_number功能

    看了一些文章,终于知道该怎么在 mysql 里面实现 row_number() 排序 话不多说,show you the code: 第一步:建表: create table grades( `nam ...

  4. CF1492B Card Deck 题解

    Content 有 \(n\) 张纸牌组成的一个牌堆,每张纸牌都有一个价值 \(p_1,p_2,\dots,p_n\).每次选出最顶上的几个牌放到另外一个一开始为空的牌堆里面.定义一个牌堆的总值为 \ ...

  5. java 多线程Thread 子类 定时器Timer

    定时器Timer, 定时器分类: 1,指定时间指定任务(明天早上8点准时提醒我起床),相当于linux里面的at命令 2,周期性的执行任务(每隔三分钟闹钟响一次),相当于Linux里面的cron命令 ...

  6. flutter 学习笔记

    常用属性 container 填充padding,边距margins,边框borders,背景色color, decoration: 渐变gradient-Alignment(x,y),圆角borde ...

  7. Linux(centos7)安装redis并设置redis开机自启动

    1.下载redis安装包 wget http://download.redis.io/releases/redis-4.0.6.tar.gz 2.解压安装包 tar -zxvf redis-4.0.6 ...

  8. RPA账户和密码管理方案

    如何将登录业务系统的账户和密码"更好的,更合适"地交给RPA? 相信很多小伙伴们在做RPA的时候, 都会或多或少的遇到类似的问题. 正常情况下IT管理人员都会给真实的业务人员分配业 ...

  9. 【论文笔记】 Popularity Bias in Dynamic Recommendation

    Popularity Bias in Dynamic Recommendation Authors: Ziwei Zhu, Yun He, Xing Zhao, James Caverlee KDD' ...

  10. 【LeetCode】108. Convert Sorted Array to Binary Search Tree 解题报告 (Java & Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 Java解法 Python解法 日期 题目地址:ht ...