Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to parse the ConcurrentHashMap source code in jdk8

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article introduces you how to analyze the ConcurrentHashMap source code in jdk8, the content is very detailed, interested friends can refer to, hope to be helpful to you.

The classes under the java.util.concurrent package are all classic.

ConcurrentHashMap is the most discussed and controversial class in java. Many people are curious about this category.

As concurrent collections, people are more concerned about reading and writing, locking, and map hashing.

How to read and write locks

Get operation

Java code download

It's obviously unlocked. Including all read operations. It's all unlocked.

Public V get (Object key) {

Node [] tab; Node e, p; int n, eh; K ek

Int h = spread (key.hashCode ())

If ((tab = table)! = null & & (n = tab.length) > 0 & &

(e = tabAt (tab, (n-1) & h))! = null) {

If ((eh = e.hash) = = h) {

If ((ek = e.key) = = key | | (ek! = null & & key.equals (ek)

Return e.val

}

Else if (eh

< 0) // TreeBin 操作 return (p = e.find(h, key)) != null ? p.val : null; while ((e = e.next) != null) { if (e.hash == h && ((ek = e.key) == key || (ek != null && key.equals(ek)))) return e.val; } } return null; } put操作(remove等修改操作) 所有读操作是这样一个模式,如果hash桶中坐标没有数据。就使用CAS 操作。如果有数据。就使用synchronized关键字。比起jdk1.7,1.6使用读写锁,代码比较简洁,同样使用cas操作比 读写锁的性呢过开销底得太多了。但是程序设计变得十分复杂,请下添加的代码 下载 Java代码 final V putVal(K key, V value, boolean onlyIfAbsent) { if (key == null || value == null) throw new NullPointerException(); int hash = spread(key.hashCode()); int binCount = 0; //为什么要这个循序,大家会很疑惑 //其实很简单,因为多线程操作,然后没有使用锁,使用 unsafe,多个unsafe不是原子性的,在多线程的情况下,会出现问题。所以使用for来解决这个问题 for (Node[] tab = table;;) { // f 是节点,n是数组长度,i是hash与数组长度的数组下标,fh是在数组下标已经坐在的节点的hash值 Node f; int n, i, fh; if (tab == null || (n = tab.length) == 0) tab = initTable();//延迟初始化 table //通过unsafe 判断 下标是否有 节点 else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { //如果不存在。就创建一个节点 通过 unsafe加入到数组中,所以读是不加锁的 //注意:如果在多个线程同时加入 hash后同一下标的node,那么只有一个会成功,其他失败。失败的就会在再次循序。 //这是循环解决的问题之一 if (casTabAt(tab, i, null, new Node(hash, key, value, null))) break; // no lock when adding to empty bin } //大家很奇怪这个 if是干什么用额,去了解ForwardingNode这个对象 //这个对象在 hash散列的时候用,原来的一个节点会重新散列到 下个表,原来表的节点的hash就成为了 moved else if ((fh = f.hash) == MOVED) tab = helpTransfer(tab, f); else { V oldVal = null; //如果节点存在,就需要添加链了。 //添加链的时候 就上锁 这个节点,那么所有在这个节点的更新操作都会上锁 //这里上锁,比双桶的开销小多了。如果设计好,可以说几乎忽略不计。 synchronized (f) { if (tabAt(tab, i) == f) { if (fh >

= 0) {

/ / add a linked list

BinCount = 1

For (Node e = f * *; + + binCount) {

K ek

If (e.hash = = hash & &

((ek = e.key) = = key | |

(ek! = null & & key.equals (ek) {

OldVal = e.val

If (! onlyIfAbsent)

E.val = value

Break

}

Node pred = e

If ((e = e.next) = = null) {

Pred.next = new Node (hash, key

Value, null)

Break

}

}

}

/ / add add Node under tree

Else if (f instanceof TreeBin) {

Node p

BinCount = 2

If ((p = (TreeBin) f) .putTreeVal (hash, key)

Value)! = null) {

OldVal = p.val

If (! onlyIfAbsent)

P.val = value

}

}

}

}

/ / java8, clear reform

If (binCount! = 0) {

/ / binCount is the number of linked list operations and the number of operations. Indicates how long the linked list is. When the linked list is greater than or equal to 8, the linked list becomes tree

If (binCount > = TREEIFY_THRESHOLD)

TreeifyBin (tab, I)

If (oldVal! = null)

Return oldVal

Break

}

}

}

AddCount (1L, binCount)

Return null

}

Here is the main point that everyone is most concerned about, re-hashing, that is, re-hash

Java code download

Final V putVal (K key, V value, boolean onlyIfAbsent) {

If (key = = null | | value = = null) throw new NullPointerException ()

Int hash = spread (key.hashCode ())

Int binCount = 0

/ / Why do you want to follow this order? people will be very confused.

/ / is actually very simple, because multithreading operation, and then do not use locks, using unsafe, multiple unsafe is not atomic, in the case of multithreading, there will be problems. So use for to solve this problem.

For (Node [] tab = table;;) {

/ / f is the node, n is the array length, I is the array subscript of hash and array length, and fh is the hash value of the node on which the array subscript is already sitting

Node f; int n, i, fh

If (tab = = null | | (n = tab.length) = = 0)

Tab = initTable (); / / delayed initialization of table

/ / determine whether the subscript has nodes by unsafe

Else if ((f = tabAt (tab, I = (n-1) & hash)) = = null) {

/ / if it does not exist. Just create a node and add it to the array through unsafe, so the read is unlocked

/ / Note: if multiple threads join hash at the same time with the same target node, only one will succeed and the others will fail. Those who fail will follow the order again.

/ / this is one of the problems solved by the loop.

If (casTabAt (tab, I, null)

New Node (hash, key, value, null)

Break; / / no lock when adding to empty bin

}

/ / people wonder what this if is for, to understand the object ForwardingNode.

/ / when this object is used in hash hashing, the original node will be rehashed to the next table, and the hash of the original table node will become moved.

Else if ((fh = f.hash) = = MOVED)

Tab = helpTransfer (tab, f)

Else {

V oldVal = null

/ / if the node exists, you need to add a chain.

/ / lock this node when you add a chain, then all update operations on this node will be locked.

/ / it's locked here, which is much less expensive than double buckets. If it is well designed, it can be said to be almost negligible.

Synchronized (f) {

If (tabAt (tab, I) = = f) {

If (fh > = 0) {

/ / add a linked list

BinCount = 1

For (Node e = f * *; + + binCount) {

K ek

If (e.hash = = hash & &

((ek = e.key) = = key | |

(ek! = null & & key.equals (ek) {

OldVal = e.val

If (! onlyIfAbsent)

E.val = value

Break

}

Node pred = e

If ((e = e.next) = = null) {

Pred.next = new Node (hash, key

Value, null)

Break

}

}

}

/ / add add Node under tree

Else if (f instanceof TreeBin) {

Node p

BinCount = 2

If ((p = (TreeBin) f) .putTreeVal (hash, key)

Value)! = null) {

OldVal = p.val

If (! onlyIfAbsent)

P.val = value

}

}

}

}

/ / java8, clear reform

If (binCount! = 0) {

/ / binCount is the number of linked list operations and the number of operations. Indicates how long the linked list is. When the linked list is greater than or equal to 8, the linked list becomes tree

If (binCount > = TREEIFY_THRESHOLD)

TreeifyBin (tab, I)

If (oldVal! = null)

Return oldVal

Break

}

}

}

AddCount (1L, binCount)

Return null

}

Private final void addCount (long x, int check) {

CounterCell [] as; long b, s

/ / the purpose of this judgment is to solve the problem of unsafu's endless cycle.

If ((as = counterCells)! = null | |

! U.compareAndSwapLong (this, BASECOUNT, b = baseCount, s = b + x)) {

CounterCell a; long v; int m

Boolean uncontended = true

If (as = = null | | (m = as.length-1)

< 0 || (a = as[ThreadLocalRandom.getProbe() & m]) == null || !(uncontended = U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) { fullAddCount(x, uncontended); return; } if (check = 0) { Node[] tab, nt; int n, sc; //我靠,这里又有一个循序,是解决什么问题了? //sc也就是 sizeCtl 等于 -1的情况只有,table初始化与序列化的时候。 //这个循环是保证 序列化之后,还可以加入数据,目测是这样,不敢保证。 while (s >

= long (sc = sizeCtl) & & (tab = table)! = null & &

(n = tab.length)

< MAXIMUM_CAPACITY) { int rs = resizeStamp(n); // 这个if 基本可以忽略 if (sc < 0) { if ((sc >

> > RESIZE_STAMP_SHIFT)! = rs | | sc = = rs + 1 | |

Sc = = rs + MAX_RESIZERS | | (nt = nextTable) = = null | |

TransferIndex > > 3) / NCPU: n)

< MIN_TRANSFER_STRIDE) stride = MIN_TRANSFER_STRIDE; // subdivide range if (nextTab == null) { // initiating try { @SuppressWarnings("unchecked") Node[] nt = (Node[])new Node[n = bound || finishing) advance = false; //这个判断只可能进来一次, else if ((nextIndex = transferIndex) >

> 3 / 8 = 2

/ / I = 128 bound-126, then it will come in every two times, then you will know, (nextIndex = transferIndex) this operation will know that multithreading is operating.

Else if (U.compareAndSwapInt

(this, TRANSFERINDEX, nextIndex

NextBound = (nextIndex > stride?

NextIndex-stride: 0)) {

Bound = nextBound

I = nextIndex-1

Advance = false

}

}

/ /

/ / that operation will make I greater than or equal to n, which is unexpected.

/ / I + n is not greater than or equal to nextn.

/ / there is only one possibility that, under strong concurrency, both threads enter. It's time to operate. That's right

/ /

If (I)

< 0 || i >

= n | | I + n > = nextn) {

Int sc

If (finishing) {

NextTable = null

Table = nextTab

SizeCtl = (n > > 1)

Return

}

/ / first, there is nothing wrong with sc-1. The judgment in addcont needs to be reduced by one, but not in addcount. If you put it here, you can reduce the operation.

If (U.compareAndSwapInt (this, SIZECTL, sc = sizeCtl, sc-1)) {

/ / this operation excludes the thread operation before the last entry.

/ / how do you do that? er, please see line 2286 of the addCount method.

If ((sc-2)! = resizeStamp (n) = 0) {

Int runBit = fh & n

Node lastRun = f

/ / find the split lastRun

For (Node p = f.nextp! = null; p = p.next) {

Int b = p.hash & n

If (b! = runBit) {

RunBit = b

LastRun = p

}

}

If (runBit = = 0) {

Ln = lastRun

Hn = null

}

Else {

Hn = lastRun

Ln = null

}

/ / split a linked list into two lists

For (Node p = f; p! = lastRun; p = p.next) {

Int ph = p.hash; K Competition = p.key; V pv = p.val

If ((ph & n) = = 0)

Ln = new Node (ph, competition, pv, ln)

Else

Hn = new Node (ph, competition, pv, hn)

}

/ / add two linked lists to the next tab

SetTabAt (nextTab, I, ln)

SetTabAt (nextTab, I + n, hn)

/ / replace the split bucket with fwd

SetTabAt (tab, I, fwd)

Advance = true

}

Else if (f instanceof TreeBin) {

TreeBin t = (TreeBin) f

TreeNode lo = null, loTail = null

TreeNode hi = null, hiTail = null

Int lc = 0, hc = 0

For (Node e = t.first; e! = null; e = e.next) {

Int h = e.hash

TreeNode p = new TreeNode

(h, e.key, e.val, null, null)

If ((h & n) = = 0) {

If ((p.prev = loTail) = = null)

Lo = p

Else

LoTail.next = p

LoTail = p

+ + lc

}

Else {

If ((p.prev = hiTail) = = null)

Hi = p

Else

HiTail.next = p

HiTail = p

+ + hc

}

}

Ln = (lc

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report