ConcurrentHashMap深度学习

190 阅读3分钟

前言

在工作中有很多场景会使用到Map接口,通常情况下使用HashMap就可以解决日常工作的很多问题,但是一些框架或者多线程处理类中,使用HashMap就可能会出现线程安全问题,多线程环境下Map也提供了线程安全的解决方案,那就是HashTable。但是除了HashTable之外还有没有其他替代方案呢?

HashTable

HashTable的底层实现原理和HashMap原理很相似,只是方法都是使用synchronized修饰的。由于synchronized是一种重量级的锁机制。所以使用HashTbale实现的线程Map在性能上而言是比较慢的。
既然说到了synchronized,那咱们继续探究下synchronized的为什么称之为“重量级”?
如下一段程序:

image.png

反编译之后:

image.png

在反编译之后发现并没有类似synchronized的字样,原因是:synchronized关键字经过Javac编译之后,会在同步块的前后分别形成 monitorenter和monitorexit这两个字节码指令。

image.png 通过这句话中的描述,大概可以猜测到,monitorenter是一个针对锁做加法的指令,可以类比为ladd指令(Long类型的自增),而monitorexit则可以类比为lsub指令(Long类型的自减)。而文中提到的在执行monitorenter时则去尝试获取对象锁,那么这个对象锁是哪里来的呢。其实jvm中每个对象都会有一个锁标识位,如图所示:

image.png

markword中就预留了锁标识位。所以在执行monitorenter时就会在这个标识位中添加标识(占坑)以此来标识持有该对象的锁。 回到之前的问题,为什么说synchironized是重量级锁呢?因为java的多线程是映射到操作系统的内核系统上的,不管是阻塞或者唤醒一个线程都是需要操作系统来帮忙完成的,因此涉及到用户态到内核态的转换,进行这种转换是很耗时的。对于一些简单的代码块,其用户态到内核态的转换的耗时可能要远远大于代码执行的时间。因此synchronized是一个重量级的锁。

ConcurrentHashMap

ConcurrentHashMap和hashTable都是属于Map的实现类,都可以保证在多线程的情况下安全性,但是他们的实现方式却截然不同,ConcurrentHashMap是属于java.utils.concurrent包下的,而hashTable是java.utils下的。

在ConcurrentHashMap的源码中,我发现其核心思想就是利用系统底层的CAS机制来保证多线程下其值不会被篡改,什么是CAS,CAS其实是Compare And Swap的简称,意思就是比较并交换,其原理就是在调用方法时传入值的地址,预期值和新值,如果在值地址上的原有值和预期值一样,则使用新值替换。当然了这些比较过程都不需要我们来完成,而是通过JNI调用底层c++来实现的。