java1.0中的容器只有Vector和HashTable这两个

  1. Vector是一个一个往里扔的,实现了List接口
  2. HashTable是一对一对往里扔的,实现了Map接口
  3. 这两个方法在设计之初的问题—->这两个容器中的所有方法都是默认加synchronized的,效率有点差
  4. 也不能说他们性能极差,因为synchronized现在的性能也并不是十分的差(重量级锁—->锁升级)
  5. 原来设计的时候,他认为所有的方法必须是线程安全的,说他们效率差正是因为多数的时候只有一个线程在运行,是没有必要加synchronized的

之后意识到没必要一定加synchronized就设计了HashMap

  1. HashMap完全没有加锁
  2. 与HashTable除了synchronized有无的区别之后,还有一些其他区别
  3. Sun在最初的HashTable上又添加了一些东西,比HashTable好用一些
  4. 但是HashMap中没有锁的东西,为了让HashMap既可以用于那些要锁的环境,又可以用于不需要锁的环境,又添加了一个Collections类—->容器的一些
  5. Collections类中有一个synchronizedMap方法,这个方法会把不加锁的HashMap变成加锁的版本
  6. HashMap有两个版本:一个是不带锁的版本,一个是通过synchronizedMap实现的带锁的版本
  7. HashTable是最早的容器,是遗留下来的容器(jdk1.0),很多设计上有一些缺陷,所以现在HashTable和Vector基本不用
  8. 面试的时候也许会从数据结构方面问一些HashTable的内容—->了解一下
  9. Vector和HashTable自带锁,但是一般不用

  1. HashMap本身不带锁,使用synchronizedMap加的锁又是很重的锁,加锁的时候是直接返回SynchronizedMap这种类型,用的锁还是synchronized—->和HashTable的效率区别不大,里面定义了一个要加锁的对象(要锁住的对象),然后使用synchronized代码块的方式进行加锁
  2. HashTable是将整个方法上全部加锁,但是SynchronizedMap的锁的粒度上就相对来说小了一些,可能能提高一点效率—->效率仍不是特别高

在JUC包出来后,又添加了一个新的效率更高的ConcurrenctHashMap

HashTable在某些情况下(在某些方面)的效率未必比其他的差(synchronized某些方面的效率未必比其他的差)

  1. 下面的程序中测试了每一个容器使用时的效率
  2. 哪个效率高,哪个效率低,不要想当然,一定要写代码来进行测试—->可以用JMH测试框架
    1. 多少个键值对(UUID对装到容器中)
    2. 多少个线程
    3. 这两个值变化会影响效率上的变化
  3. 性能测试有时候需要程序员来写—->一般测试开发来写,但是一般开发的程序员也需要了解性能测试这一块的—->小公司只能自己测试
  4. 做测试的时候,把要用到的数据先生成好,而不是到真正要用的时候才去生成这个数据,因为测试的时候用到的测试用例应该是完全一样的(是同样的100万对),要是每次重新生成,会生成不一样的内容,这样测试的时候就会有一些干扰因素
  5. 测试用例得先准备好,测不同的东西的时候要用同样的测试用例才能会有更精确的区别;用不同的测试用例,看不出是因为测试用例不一样的原因,还是因为容器不一样的原因,会区分不开—->**控制变量**

Constants常量类

  1. package com.mashibing.juc.c_023_02_FromHashtableToCHM;
  2. public class Constants {
  3. public static final int COUNT = 1000000;
  4. public static final int THREAD_COUNT = 100;
  5. }

HashTable

  1. package com.mashibing.juc.c_023_02_FromHashtableToCHM;
  2. import java.util.Hashtable;
  3. import java.util.UUID;
  4. public class T01_TestHashtable {
  5. static Hashtable<UUID, UUID> m = new Hashtable<>();
  6. static int count = Constants.COUNT;
  7. static UUID[] keys = new UUID[count];
  8. static UUID[] values = new UUID[count];
  9. static final int THREAD_COUNT = Constants.THREAD_COUNT;
  10. static {
  11. for (int i = 0; i < count; i++) {
  12. keys[i] = UUID.randomUUID();
  13. values[i] = UUID.randomUUID();
  14. }
  15. }
  16. static class MyThread extends Thread {
  17. int start;
  18. int gap = count/THREAD_COUNT;
  19. public MyThread(int start) {
  20. this.start = start;
  21. }
  22. @Override
  23. public void run() {
  24. for(int i=start; i<start+gap; i++) {
  25. m.put(keys[i], values[i]);
  26. }
  27. }
  28. }
  29. public static void main(String[] args) {
  30. long start = System.currentTimeMillis();
  31. Thread[] threads = new Thread[THREAD_COUNT];
  32. for(int i=0; i<threads.length; i++) {
  33. threads[i] =
  34. new MyThread(i * (count/THREAD_COUNT));
  35. }
  36. for(Thread t : threads) {
  37. t.start();
  38. }
  39. for(Thread t : threads) {
  40. try {
  41. t.join();
  42. } catch (InterruptedException e) {
  43. e.printStackTrace();
  44. }
  45. }
  46. long end = System.currentTimeMillis();
  47. System.out.println(end - start);
  48. System.out.println(m.size());
  49. //-----------------------------------
  50. start = System.currentTimeMillis();
  51. for (int i = 0; i < threads.length; i++) {
  52. threads[i] = new Thread(()->{
  53. for (int j = 0; j < 10000000; j++) {
  54. m.get(keys[10]);
  55. }
  56. });
  57. }
  58. for(Thread t : threads) {
  59. t.start();
  60. }
  61. for(Thread t : threads) {
  62. try {
  63. t.join();
  64. } catch (InterruptedException e) {
  65. e.printStackTrace();
  66. }
  67. }
  68. end = System.currentTimeMillis();
  69. System.out.println(end - start);
  70. }
  71. }

HashMap不是同步的—->有可能会报异常

  1. 因为里面会将HashMap中的Node变成TreeNode?
  2. 内部没有锁,所以多线程访问的时候会出问题,往里插没有实际意义

使用SynchronizedMap给HashMap手动加锁

  1. 底层是自己定义了一个mutex的对象,然后通过synchronized对mutex加锁
  2. 严格意义上讲,这种方式和HashTable的效率差别不会太大—->加锁的粒度不同,本质上都是通过synchronized

实际中在多线程中真正用得最多的:ConcurrentHashMap

  1. 以后实际中多线程一般用的都是ConcurrentHashMap
  2. ConcurrentHashMap的效率比HashTable和SynchronizedMap高?
    1. ConcurrentHashMap的效率主要提高在读上面
    2. 往里面插的时候,内部做了多种判断,本来是链表,到8之后还要变成红黑树,里面还做了各种CAS的判断,所以往里面插的效率会更低一点
    3. HashTable和SynchronizedMap虽然读的时候效率低一些,但是往里面插的时候只需要加锁就行了,没有那么复杂的判断
    4. 要用哪一个数据结构,还是要看实际中的需求


ConcurrentHashMap的效率提升在读上面,而不是在写上面,写的时候反而是HashTable和SynchronizedMap效率更高—->在实际中选择用哪一种的时候往往要进行压测,以实测为准

  1. HashTable读的时间是32s
  2. SynchronizedMap读的时间是38s
  3. ConcurrentHashMap读的时间是1.7s

  1. HashTable写的时间在1s以内
  2. SynchronizedMap写的时间在1s以内
  3. ConcurrentHashMap写的时间超过了1s

  1. 这就是为什么用ConcurrentHashMap的原因
  2. ConcurrentHashMap并发插入的时候效率未必会比HashTable和SynchronizedMap效率高
  3. ConcurrentHashMap读取的时候效率特别高

多数情况下,写的情况比较少,多数读的情况比较多

从Vector发展到ConcurrentList基本上也是这样一个发展过程(历程)

  1. 发展过来,他们的区别和HashTable发展到ConcurrentHashMap的区别差不多