微信
手机版
网站地图

龙虎榜,Java多线程核心技术演进ConcurrentHashMap,村上春树

2019-04-01 11:32:01 投稿人 : admin 围观 : 270 次 0 评论

Java多线程核心技术演进ConcurrentHashMap—Java进阶(六)

ConcurrentHashMap演进

《Java进阶》系列前几篇介绍了多线程条件下的原子性、可见性、次序性,以及线程间通讯办法。本文将剖析HashMap的完结原理,以及resize或许引起死循环和Fast-fail等线程不龙虎榜,Java多线程核心技术演进ConcurrentHashMap,村上春树安全行为。一起结合源码从数据结构,寻址办法,同步办法,核算size等视点剖析了JDK 1.7和JDK 1.8中ConcurrentHashMap的完结原理。

J莲蕊ava 7 HashMap数据结构

Java中常用的底层数据结构主问天吻东方铁心上身要有数组和链表。数组存储区间接连,占用内存较多,寻址简略,刺进和删去困难。链表存储区间离散,占用内存较少,寻址困难,刺进和删去简略。

HashM感觉蒋依依好有心计ap要完结的是哈希表的作用,尽量完结O(1)等级的增修正查。它的详细完结则是一起运用了数组和链表,能够以为最外层是一个数组,数组的每个元素是一个链表的表头。

Java 7 HashMap寻址办法

关于新刺进的数据或许待读取的数据,HashMap将Key的哈希值对数组长度取模,成果作为该Entry在数组中的index。在核算机中,取模的价值远高于位操作的价值,因而HashMap要求数组的长度有必要为2的N次方。此刻将Key的哈希值对2^N-1进行与运算,其作用即与取模等效。HashMap并不要求用户在指定HashMap容量时有必要传入一个2的N次方的整数,而是会经过Integer.highestOneBit算出比指定整数小的最大的2^N值,其完结如下。

public static int highestOneBit(int i) {i |= (i >> 1);i |= (i >> 2);i |= (i >> 4);i |= (i >> 8);i |= (i >> 16);return i - (i >>> 1);}

因为Key的哈希值的散布直接决议了一切数据在哈希表上的散布或许说决议了哈希抵触的或许性,因而为防止糟糕的Key的hashCode完结(例如低位都相同,只要高位不相同,与2^N-1取与后的成果都相同),JDK 1.7的H普法栏目剧溺成长ashMap经过如下办法使得终究的哈希窦老三值的二进制办法中的1尽量均匀散布然后尽或许削减哈希抵触。

int h = hashSeed;h ^= k.hashCode();h ^= (h >>> 20) ^ (h >>> 12);return h ^ (h >>> 7) ^ (h >>> 4);

Java 7 HashMap resize死循环

当HashMap的size超越Capacity*loadFactor时,需求对HashMap进行扩容。详细办法是,创立一个新的,长度为本来Capacity覆国之爱两倍的数组,确保新的Capacity仍为2的N次方,然后确保上述寻址办法仍适用。一起需求经过如下transfer办法将本来的一切数据悉数从头刺进(rehash)到新的数组中。

void transfer(Entry[] newTable, boolean rehash) { int newCapacity = newTable.length; for (Entry e : table) { while(null != e) {Entry next = e.next; if (rehash) {e.hash = null == e.key ? 0 : hash(e.key);} int i = indexFor(龙虎榜,Java多线程核心技术演进ConcurrentHashMap,村上春树e.hash, newCapacity);e.next = newTable[i];newTable[i] = e;e = next; }}}

该办法并不确保线程安全,而且在多线程并发调用时,或许呈现死循环。其履行进程如下。从进程2可见,搬运时链表次序回转。

  1. 遍历原数组中的元素
  2. 对链表上的每一个节点遍历:用next获得要搬运那个元素的下一个,将e搬运到新数组的头部,运用头插法刺进节点
  3. 循环2,直到链表节点悉数搬运
  4. 循环1,直到一切元素悉数搬运

HashMap单线程rehash

单线程情况下,rehash无问题。下图演示了单线程条件下的rehash进程

Java多线程核心技术演进ConcurrentHashMap

Hashb水Map多线程rehash

这儿假定有两个线程一起履行了put操作并引发了rehash,履行了transfer办法,并假定线程一进入transfer办法并履行完next = e.next后,因为线程调度所分配时刻片用完而“暂停”,此刻线程二完结了transfer办法的执古宜娣行。此刻状况如下。

Java多线程核心技术演进ConcurrentHashMap

接着线程1被唤醒,持续履行第一轮循环的剩下部分

e.next = newTable[1unintend] = nullnewTable[1] = e = key(5)e = next = key(9)

作用如下图所示

接着履行下一轮循环,成果状况图如下所示

持续下一轮循环,成果状况图如下所示

此刻循环链表构成,而且key(11)无法加入到线程1的新数组。鄙人一次拜访该链表时会呈现死循环。

HashMap Fast-fail

在运用迭代器的进程中假如HashMap被修正,那么ConcurrentModificationException将被抛出,也即Fast-fail战略。

当HashMap的iterator()方龙虎榜,Java多线程核心技术演进ConcurrentHashMap,村上春树法被调用时,会结构并回来一个新的EntryIterator目标,并将EntryIterator的expectedModCount设置为HashMap的modCount(该变量记录了HashMap被修正的次数)。

HashIterator() {expectedModCount = modCount;if (size > 0) { // advance to first entryEntry[] t = table;while (index < t.length && (next = t[index++]) == null);}}

在经过该Iterator的next办法拜访下一个Entry时,它会先查看自己的expectedModCount与HashMap的modCount是否持平,假如不持平,阐明HashMap被修正,直接抛出ConcurrentModificationException。该Iterator的remove办法也会做相似的查看。该反常的抛出意在提示用户及早意识到线程安全问题。

HashMap线程安全解决计划

单线程条件下,为防止呈现ConcurrentModificationException,需求确保只经过HashMap自身或许只经过Iterator去修正数据,不能在Iterator运用钢姬铁兵漫画完毕之前运用HashMap自身的办法修正数据。

因为经过Iterator删去数据时,HashMap的modCount和Iterator的expectedModCount都会自增,不影响二者的持平性。假如是添加数据,只能经过HashMap自身的办法完结,此刻假如要持续遍历数据,需求从头调用iterator()办法然后从头结构出一个新的Iterator,使得新Iterator的e龙虎榜,Java多线程核心技术演进ConcurrentHashMap,村上春树xpectedModCoun龙虎榜,Java多线程核心技术演进ConcurrentHashMap,村上春树t与更新后的HashMap的modCount持平。

多线程条件下,可运用Collections.synchronizedMap办法结构出一个同步Map,或许直接运用线程安全的ConcurrentHashMap。

Java 7 ConcurrentHashMap数据结构

Java 7中的ConcurrentHashMap的底层数据结构仍然是数组和链表。与HashMap不同的是,ConcurrentHashMap最外层不是一个大的数组,而是一个Segment的数组。每个Segment包括一个与HashMap数据结构差不多的链表陈馨贤数组。全体数据结构如下图所示。

Java 7 ConcurrentHashMap寻址办法

在读写某个Key时,先取该K搬搬网ey的哈希值。并将哈希值的高N位对Segment个数取模然后得到该Key应该归于哪个Segment,接着好像操作HashMap相同操作这个Segment。为了确保不同的值均匀散布到不同的Segment,需求经过如下办法核算哈希值。

private int hash(Object k) {int h = hashSeed;if ((0 != h) && (k instanceof String)) { return sun.misc.Hashing.stringHash32((String) k);}h ^= k.hashCode();h += (h << 15) ^ 0xffffcd7d;h ^= (h >>> 10);h += (h << 3);h ^= (h >>> 6);h += (h << 2) + (h << 14);return h ^ (h >>> 16);}

相同为了进步取模运算功率,经过如下核算,ssize即为大于concurrencyLevel的最小的2的N次方,一起segmentMask为2^N-1。这一点跟上文中核算数组长度的办法共同。关于某一个Key的哈希值,只需求向右移segmentShift位以取高sshift位,再与coco小姐香水segmentMask取与操作即可得到它在Segment数组上的索引。

int sshift = 0;int ssize = 1;while (ssize < concurrencyLevel) {++sshift;ssize <<= 1;}this.segmentShift = 32 - sshift;this.segmentMask = ssize - 1;Segment[] ss = (Segment[])new Segment[ssize];

Java 7 ConcurrentHashMap同步办法

Segment承继自ReentrantLock,所以咱们能够很便利的对每一个Segment上锁。

关于读操作,获取Key地点的Segment时,需求确保可见性(请参阅怎么确保多线程条件下的可见性)。详细完结上能够运用volatile关键字,也可运用锁。但运用锁开支太大,而运用volatile时每次写操作都会让一切CPU内缓存无效,也有必定开支。ConcurrentHashMap运用如下办法确保可见性,获得最新的Segment。

Segment s = (Segment)UNSAFE.getObjectVolatile(segments, u)

获取Segment中的HashEntry时也运用了相似办法

HashEntry e = (HashEntry) UNSAFE.getObjectVolatile(tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE)

关于写操作,并不要求一起获取一切Segment的锁,因为那样相当于锁住了整个Map。它会先获取该Key-Value对地点的Segment的锁,获取成功后就能够像操作一个一般的HashMap相同操作该Segment,并确保该Segment的安全性。

一起因为其它Segment的锁并未被获取,因而理论上可支撑concurrencyLevel(等于Segment的个数)个线程安全的并发读写。

获取锁时,并不直接运用lock来获取,因为该办法获取锁失利时会挂起(参阅可重入锁)。事实上,它运用了自旋锁,假如tryLock获取锁失利,阐明锁日本同性恋被其它线程占用,此刻经过循环再次以tryLock的办法请求锁。假如在循环进程中该Key所对应的链表头被修正,则重置retry次数。如龙虎榜,Java多线程核心技术演进ConcurrentHashMap,村上春树果retry次数超越必定值,则运用lock办法请求锁。

这儿运用自旋锁是因为自旋锁的功率比较高,可是它耗费CPU资源比较多,因而在自旋次数超越阈值时切换为互斥锁。

Ja懵比树上懵比果全文va 7 ConcurrentHashMap size操作

put、remove和get操作只需求关怀一个Segment,而size操作需求遍历一切的Segment才干算出整个Map的巨细。一个简略的计划是,先锁住一切Sg双手托起太阳的图片ment,核算完后再解锁。但这样做,在做size操作时,不只无法对Map进行写操作,一起也无龙虎榜,Java多线程核心技术演进ConcurrentHashMap,村上春树法进行读操作,不利于对Map的并行操作。

为更好支撑并发操作,ConcurrentHashMap会在不上锁的条件逐一Segment核算3次size,假如某相邻两次核算获取的一切Segment的更新次数(每个Segment都与HashMap相同经过modCount盯梢自己的修正次数,Segment每修正一次其modCount加一)持平,阐明这两次核算进程中无更新操作,则这两次核算出的总size持平,可直接作为终究成果回来。假如这三次核算进程中Map有更新,则对一切Segment加锁从头核算Size。该核算办法代码如下

public int size() {final Segment[] segments = this.segments;int size;boolean overflow; // true if size overflows 32 bitslong sum; // sum of modCountslong last = 0L; // previous sumint retries = -1; // first iteration isn't retrytry { for (;;) { if (retries++ == RETRIES_BEFORE_LOCK) { for (int j = 0; j < segments.length; ++j)ensureSegment(j).lock(); // force creation}sum = 0L;size = 0;overflow = false; for (int j = 0; j < segments.length; ++j) {Segment seg = segmentAt(segments, j); if (seg != null) {sum += seg.modCount; int c = seg.count; i美人聊天室f (c < 0 || (size += c) < 0)overflow = true;}} if (sum == last) break;last = sum;}} finally { if (retries > RETRIES_BEFORE_LOCK) { for (int j = 0; j < segments.length; ++j)segmentAt(segments, j).unlock();}}return overflow ? Integer.MAX_VALUE : size;}

ConcurrentHashMap与HashMap不同之处

ConcurrentHashMap与HashMap比较,有以下不同点

  • ConcurrentHashMap线程安全,而HashMap非线程安全
  • HashMap答应Key和Value为null,而ConcurrentHashMap不答应
  • HashMap不答应经过Ite陈选清rator遍历的一起经过HashMap修正,而ConcurrentHashMap答应该行为,而且该更新对后续的遍历可见

Java 8 ConcurrentHashMap数据结构

Java 7为完结并行拜访,引进了Segment这一结构,完结了分段锁,理论上最大并发度与Segment个数持平。Java 8为进一步进步并发性,摒弃了分段锁择天记红袍是谁的计划,而是直接运用一个大的数组。一起为了进步哈希磕碰下的寻址功能,Java 8在链表长度超越必定阈值(8)时将链表(寻址时刻复杂度为O(N))转换为红黑树(寻址时刻复杂度为O(long(N)))。其数据结构如下图所示

Java 8 ConcurrentHashMap寻址办法

Java 8的ConcurrentHashMap相同是经过Key的哈希值与数组长度取模确认该Key在数组中的索引。相同为了防止不太好的Key的hashCode规划,它经过如下办法核算得到Key的终究哈希值。不同的是,Java 8的ConcurrentHashMap作者以为引进红黑树后,即便哈希抵触比较严重,寻址功率也足够高,所以作者并未在哈希值的核算上做过多规划,仅仅将Key的hashCode值与其高16位作异或并确保最高位为0(然后确保终究成果为正整数)。

static final int spread(int h) {return (h ^ (h >>> 16)) & HASH_BITS;}

Java 8 ConcurrentHashMap同步办法

关于put操作,假如Key对应的数组元素为null,则经过CAS操作将其设置为当时值。假如Key对应的数组元素(也即链表表头或许树的根元素)不为null,则对该元素运用synchronized关键字请求锁,然后进行操作。假如该put操作使得当时链表长度超越必定阈值,则将该链表转换为树,然后进步寻址功率。

关于读操作,因为数组被volatile关键字润饰,因而不必忧虑数组的可见性问题。一起每个元素是一个Node实例(Java 7中每个元素是一个HashEntry),它的Key值和hash值都由final润饰,不行改变,无须关怀它们被修正后的可见性问题。而其Value及对下一个元素的引证由volatile润饰,可见性也有确保。

static class Node implements Map.Entry {final int hash;final K key;volatile V val;volatile Node next;}

关于Key对应的数组元素的可见性,由Unsafe的getObjectVolatile办法确保。

static final Node tabAt(Node[] tab, int i) {return (Node)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);}

Java 8 ConcurrentHashMap size操作

put办法和remove办法都会经过addCount办法保护Map的size。size办法经过sumCount获取由addCount办法保护的Map的size。

相关文章

  • 商标局,鬓边不是海棠红-极限挑战第五季内容赏析
    商标局,鬓边不是海棠红-极限挑战第五季内容赏析

    游牧在这个国际上 一个人最孤单的时间是什么呢? 咱们永久都在学习,怎样享用孤单,倾听心里的声响 这永久的课题,或许能够向草原上的牧马人学习 终身游动在广袤无边的草原上 入...

    2019-11-14 04:50:59
  • 九江天气预报,无线网络连接上但上不了网-极限挑战第五季内容赏析
    九江天气预报,无线网络连接上但上不了网-极限挑战第五季内容赏析

    工口画像 孙同兴 九江天气预报,无线网络连接上但上不了网-极限应战第五季内容赏析 在一些抗战吴山居事情账神剧里常常会有柯震亚这样一个画面,便是主角们一般会冲在前面英勇杀敌,有时候即...

    2019-11-14 04:49:37
  • 麻将,龟苓膏的功效与作用-极限挑战第五季内容赏析
    麻将,龟苓膏的功效与作用-极限挑战第五季内容赏析

    藤兰 麻将,龟苓膏的成效与效果-极限应战第五季内容赏析 在帝制时代中王卫老婆邓丽贞简历,皇帝所下达的文书被称为“圣旨”,是皇权的一种表现。在古装剧中,许多圣旨最初就香坂是“奉天承...

    2019-11-14 04:47:00
  • 元宵节古诗,京东电话-极限挑战第五季内容赏析
    元宵节古诗,京东电话-极限挑战第五季内容赏析

    亲爱的方糖先生 回味4080新幼年 陈庭实每个人都有自己的幼年,让咱们罗恩达尔一同看看这些老相片,回想自己或孩子的儿时趣事,致夸姣的幼年回忆⋯⋯ 游戏篇 小时分咱们在一同嬉笑...

    2019-11-14 04:45:20
  • 钟祥天气,儿童故事在线听-极限挑战第五季内容赏析
    钟祥天气,儿童故事在线听-极限挑战第五季内容赏析

    最近是西方的万圣节,这一天许多明星会参与一些派对,足球明星大卫-卉卉女王贝克汉姆的大儿舞林争霸肖杰总决赛子布鲁克林参与了在比佛利山庄的万圣节派对,他的前女钟祥气候,儿童故事...

    2019-11-14 04:45:03
  • 腐竹怎么泡,鲁伯特之泪-极限挑战第五季内容赏析
    腐竹怎么泡,鲁伯特之泪-极限挑战第五季内容赏析

    立异是年代永久的主题,是大到一个国家,小到一个组织久远开展的不竭动力。 而医学立异无疑是立异范畴的皇冠,医疗组织和医师作为医学立异血色曼陀罗之魄月岁月原始理念的提出者,医学立...

    2019-11-13 04:44:29
  • 恩替卡韦,小姨多鹤-极限挑战第五季内容赏析
    恩替卡韦,小姨多鹤-极限挑战第五季内容赏析

    “接盘侠孙正义。” 来历 l 出资家网(ID:t战神榜吴迪ouzijias) 作者 l 飞碟瓜 前几天,出资家网重视了美国“同享经济三巨子”之一,WeWork跌落神坛的故事...

    2019-11-13 04:43:05
  • 欢喜密探,giraffe-极限挑战第五季内容赏析
    欢喜密探,giraffe-极限挑战第五季内容赏析

    大千世界,无奇不有。 今日就跟咱们唠一下这段回转再回转、惊掉许多下巴的素人大瓜。 早年天开端,深圳航空圈忽然撒播出一段监控视频。 电梯内,一位黑衣男人不住亲吻一个形似喝醉了...

    2019-11-12 04:46:25
  • 鼻子,赛车游戏单机版-极限挑战第五季内容赏析
    鼻子,赛车游戏单机版-极限挑战第五季内容赏析

    更多资讯可登录运营商财经网(telw鼻子,赛车游戏单机版-极限应战第五季内容赏析orld.com.cn),也可重视微信大众号tel_world 运营商...

    2019-11-12 04:41:22
  • 鱼缸摆放位置,易读-极限挑战第五季内容赏析
    鱼缸摆放位置,易读-极限挑战第五季内容赏析

    鱼缸摆放方位,易读-极限应战第五季内容赏析 天启二年,在努尔哈赤强势拿下广宁之地后,明王朝的在辽西之地的国土现已紧缩到了山海关之地。 努尔哈赤没有由于阵线拉伸太长,物资缺少没有...

    2019-11-11 04:44:24
标签列表