ConcurrentHashMap的底层实现

并发基础概念

内存模型

Memory分为两类,main memory和working memory,main memory为所有线程共享,working memory中存放的是线程所需要的变量的拷贝(线程要对main memory中的内容进行操作的话,首先需要拷贝到自己的working memory,一般为了速度,working memory一般是在cpu的cache中的)。volatile的变量在被操作的时候不会产生working memory的拷贝,而是直接操作main memory,当然volatile虽然解决了变量的可见性问题,但没有解决变量操作的原子性的问题,这个还需要synchronized或者CAS相关操作配合进行。

 

 

可见性

共享变量:如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量

可见性:一个线程对共享变量值的修改,能够及时的被其他线程看到

 

 

 

假设一个对象中有一个变量i,那么i是保存在main memory中的,当某一个线程要操作i的时候,首先需要从main memory中将i 加载到这个线程的working memory中,这个时候working memory中就有了一个i的拷贝,这个时候此线程对i的修改都在其working memory中,直到其将i从working memory写回到main memory中,新的i的值才能被其他线程所读取。从某个意义上说,可见性保证了各个线程的working memory的数据的一致性。 可见性遵循下面一些规则:

· 当一个线程运行结束的时候,所有写的变量都会被flush回main memory中。

· 当一个线程第一次读取某个变量的时候,会从main memory中读取最新的。

· volatile的变量会被立刻写到main memory中的,在jsr133中,对volatile的语义进行增强,后面会提到

· 当一个线程释放锁后,所有的变量的变化都会flush到main memory中,然后一个使用了这个相同的同步锁的进程,将会重新加载所有的使用到的变量,这样就保证了可见性。

 

 

 

内存可见性------关键字volatile

 

 

 

 

 

原子性

原子性就是当某一个线程修改i的值的时候,从取出i到将新的i的值写给i之间不能有其他线程对i进行任何操作。也就是说保证某个线程对i的操作是原子性的,这样就可以避免数据脏读。 通过锁机制或者CAS(Compare And Set 需要硬件CPU的支持)操作可以保证操作的原子性。

 

 

 

ConCurrentHashMap实现原理

ConcurrentHashMap采用了非常精妙的"分段锁"策略ConcurrentHashMap的主干是个Segment数组

Segment继承了ReentrantLock,所以它就是一种可重入锁(ReentrantLock)。在ConcurrentHashMap,一个Segment就是一个子哈希表Segment里维护了一个HashEntry数组,并发环境下,对于不同Segment的数据进行操作是不用考虑锁竞争的。HashEntry是目前我们提到的最小的逻辑处理单元了。一个ConcurrentHashMap维护一个Segment数组,一个Segment维护一个HashEntry数组。

一个ConcurrentHashMap由多个segment组成,每一个segment都包含了一个HashEntry数组的hashtable, 每一个segment包含了对自己的hashtable的操作,比如get,put,replace等操作,这些操作发生的时候,对自己的hashtable进行锁定。由于每一个segment写操作只锁定自己的hashtable,所以可能存在多个线程同时写的情况,性能无疑好于只有一个hashtable锁定的情况。

 

final V remove(Object key, int hash, Object value) {

            if (!tryLock())

                scanAndLock(key, hash);

            V oldValue = null;

            try {

                HashEntry<K,V>[] tab = table;

                int index = (tab.length - 1) & hash;

                HashEntry<K,V> e = entryAt(tab, index);

                HashEntry<K,V> pred = null;

                while (e != null) {

                    K k;

                    HashEntry<K,V> next = e.next;

                    if ((k = e.key) == key ||

                        (e.hash == hash && key.equals(k))) {

                        V v = e.value;

                        if (value == null || value == v || value.equals(v)) {

                            if (pred == null)

                                setEntryAt(tab, index, next);

                            else

                                pred.setNext(next);

                            ++modCount;

                            --count;

                            oldValue = v;

                        }

                        break;

                    }

                    pred = e;

                    e = next;

                }

            } finally {

                unlock();

            }

            return oldValue;

        }

在ConcurrentHashMap的remove,put操作还是比较简单的,都是将remove或者put操作交给key所对应的segment去做的,所以当几个操作不在同一个segment的时候就可以并发的进行。

上面的代码中关于volatile类型的变量count值得一提,这里充分利用了Java 5中对volatile语义的增强,count = c的操作必须在modCount,table等操作的后面,这样才能保证这些变量操作的可见性。 Segment类继承于ReentrantLock,主要是为了使用ReentrantLock的锁,ReentrantLock的实现比 synchronized在多个线程争用下的总体开销小

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值