admin管理员组文章数量:1437317
JUC并发—8.并发安全集合二
8.ConcurrentHashMap的分段锁统计元素数据
(1)ConcurrentHashMap维护数组元素个数思路
(2)ConcurrentHashMap维护数组元素个数流程
(3)维护数组元素个数的addCount()方法
(4)维护数组元素个数的fullAddCount()方法
(5)获取数组元素个数的size()方法
(1)ConcurrentHashMap维护数组元素个数思路
当调用完put()方法后,ConcurrentHashMap必须增加当前元素的个数,以方便在size()方法中获得存储的元素个数。
在常规的集合中,只需要一个全局int类型的字段保存元素个数即可。每次添加一个元素,就对这个size变量 + 1。
在ConcurrentHashMap中,需要保证对该变量修改的并发安全。如果使用同步锁synchronized,那么性能开销比较大,不合适。所以ConcurrentHashMap使用了自旋 + 分段锁来维护元素个数。
(2)ConcurrentHashMap维护数组元素个数流程
ConcurrentHashMap采用了两种方式来保存元素的个数。当线程竞争不激烈时,直接使用baseCount + 1来增加元素个数。当线程竞争比较激烈时,构建一个CounterCell数组,默认长度为2。然后随机选择一个CounterCell,针对该CounterCell中的value进行保存。
增加元素个数的流程如下:
(3)维护数组元素个数的addCount()方法
addCount()方法的作用主要包括两部分:
一.累加ConcurrentHashMap中的元素个数
二.通过check >= 0判断是否需要进行数组扩容
其中增加数组元素个数的核心逻辑是:
首先通过CAS修改全局成员变量baseCount来进行累加。注意会先判断(as = counterCells) != null,再尝试对baseCount进行累加。这是因为如果一个集合发生过并发,那么后续发生并发的可能性会更大。如果CAS累加baseCount失败,则尝试使用CounterCell来进行累加。
代码语言:javascript代码运行次数:0运行复制public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V>, Serializable {
...
//Base counter value, used mainly when there is no contention,
but also as a fallback during table initialization races. Updated via CAS.
private transient volatile long baseCount;
//Table of counter cells. When non-null, size is a power of 2.
private transient volatile CounterCell[] counterCells;
private static final long BASECOUNT;
static {
try {
U = sun.misc.Unsafe.getUnsafe();
...
BASECOUNT = U.objectFieldOffset(k.getDeclaredField("baseCount"));
...
} catch (Exception e) {
throw new Error(e);
}
}
//Maps the specified key to the specified value in this table.
//Neither the key nor the value can be null.
public V put(K key, V value) {
return putVal(key, value, false);
}
final V putVal(K key, V value, boolean onlyIfAbsent) {
...
//调用addCount()方法统计Node数组元素的个数
addCount(1L, binCount);
return null;
}
//Adds to count, and if table is too small and not already resizing, initiates transfer.
//If already resizing, helps perform transfer if work is available.
//Rechecks occupancy after a transfer to see if another resize is already needed because resizings are lagging additions.
//x是要增加的数组元素个数
private final void addCount(long x, int check) {
CounterCell[] as; long b, s;
//首先通过CAS修改全局成员变量baseCount来进行累加
//注意:这里先判断(as = counterCells) != null,再尝试对baseCount进行CAS累加
//这是因为如果一个集合发生过并发,那么后续发生并发的可能性会更大,这种思想在并发编程中很常见
if ((as = counterCells) != null || !UpareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
//增加数组元素个数
CounterCell a; long v; int m;
boolean uncontended = true;
//如果CAS修改baseCount失败,则尝试使用CounterCell来进行累加
//1.as == null,说明CounterCell数组还没初始化
//2.(m = as.length - 1) < 0,说明CounterCell数组还没初始化
//3.(a = as[ThreadLocalRandom.getProbe() & m]) == null,说明CounterCell数组已经创建了,
//但是Hash定位到的数组位置没有对象实例,说明这个数字还存在没有CounterCell实例对象的情况
//4.如果UpareAndSwapLong(a, CELLVALUE, v = a.value, v + x)返回false,说明存在多线程竞争
if (as == null || (m = as.length - 1) < 0
|| (a = as[ThreadLocalRandom.getProbe() & m]) == null
|| !(uncontended = UpareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
//调用fullAddCount()方法实现数组元素个数的累加
fullAddCount(x, uncontended);
return;
}
if (check <= 1) {
return;
}
//sumCount()方法返回总的元素个数,也就是CounterCell数组的元素个数和baseCount两者的和
s = sumCount();
}
if (check >= 0) {
//处理数组扩容
Node<K,V>[] tab, nt; int n, sc;
while (s >= (long)(sc = sizeCtl) && (tab = table) != null && (n = tab.length) < MAXIMUM_CAPACITY) {
int rs = resizeStamp(n);
if (sc < 0) {
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
transferIndex <= 0) {
break;
}
if (UpareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
transfer(tab, nt);
}
} else if (UpareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)) {
transfer(tab, null);
}
s = sumCount();
}
}
}
final long sumCount() {
CounterCell[] as = counterCells; CounterCell a;
long sum = baseCount;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null) {
sum += a.value;
}
}
}
return sum;
}
...
}
(4)维护数组元素个数的fullAddCount()方法
fullAddCount()方法的作用主要包括三部分:初始化CounterCell数组、增加数组元素个数、对CounterCell数组扩容。
注意:为了定位当前线程添加的数组元素个数应落到CounterCell数组哪个元素,会使用ThreadLocalRandom确定当前线程对应的hash值,由该hash值和CounterCell数组大小进行类似于取模的位与运算来决定。
代码语言:javascript代码运行次数:0运行复制public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V>, Serializable {
...
//Spinlock (locked via CAS) used when resizing and/or creating CounterCells.
private transient volatile int cellsBusy;
//Table of counter cells. When non-null, size is a power of 2.
private transient volatile CounterCell[] counterCells;
//x是要增加的数组元素个数
private final void fullAddCount(long x, boolean wasUncontended) {
//通过ThreadLocalRandom来确定当前线程对应的hash值
int h;
if ((h = ThreadLocalRandom.getProbe()) == 0) {
ThreadLocalRandom.localInit();//force initialization
h = ThreadLocalRandom.getProbe();
wasUncontended = true;
}
boolean collide = false;// True if last slot nonempty
for (;;) {
CounterCell[] as; CounterCell a; int n; long v;
//(as = counterCells) != null && (n = as.length) > 0,表示counterCells数组已经完成初始化
if ((as = counterCells) != null && (n = as.length) > 0) {
//第二部分:增加数组元素个数,分两种情况
if ((a = as[(n - 1) & h]) == null) {
//情况一:(a = as[(n - 1) & h]) == null,表示将当前线程定位到counterCells数组的某位置的元素为null
//此时直接把当前要增加的元素个数x保存到新创建的CounterCell对象,然后将该对象赋值到CounterCell数组的该位置即可
if (cellsBusy == 0) {//Try to attach new Cell
//先创建一个CounterCell对象,并把x保存进去
CounterCell r = new CounterCell(x);//Optimistic create
//UpareAndSwapInt(this, CELLSBUSY, 0, 1)返回true,表示当前线程占有了锁
if (cellsBusy == 0 && UpareAndSwapInt(this, CELLSBUSY, 0, 1)) {
boolean created = false;
try {//Recheck under lock
CounterCell[] rs; int m, j;
if ((rs = counterCells) != null && (m = rs.length) > 0 && rs[j = (m - 1) & h] == null) {
//把新构建的保存了元素个数x的CounterCell对象保存到rs[j]的位置
rs[j] = r;
created = true;
}
} finally {
cellsBusy = 0;
}
if (created) {
break;
}
continue;//Slot is now non-empty
}
}
collide = false;
} else if (!wasUncontended) {//CAS already known to fail
wasUncontended = true;//Continue after rehash
} else if (UpareAndSwapLong(a, CELLVALUE, v = a.value, v + x)) {
//情况二:如果将当前线程定位到counterCells数组的某位置的元素不为null,
//那么直接通过UpareAndSwapLong(a, CELLVALUE, v = a.value, v + x)操作,对counterCells数组的指定位置进行累加
break;
} else if (counterCells != as || n >= NCPU) {
collide = false;//At max size or stale
} else if (!collide) {
collide = true;
} else if (cellsBusy == 0 && UpareAndSwapInt(this, CELLSBUSY, 0, 1)) {
//第三部分:counterCells数组扩容
//需要先通过cellsBusy == 0 && UpareAndSwapInt(this, CELLSBUSY, 0, 1),抢占锁
try {
if (counterCells == as) {// Expand table unless stale
//在原有的基础上扩容一倍
CounterCell[] rs = new CounterCell[n << 1];
//通过for循环进行数据迁移
for (int i = 0; i < n; ++i) {
rs[i] = as[i];
}
//把扩容后的对象赋值给counterCells
counterCells = rs;
}
} finally {
//恢复标识
cellsBusy = 0;
}
collide = false;
continue;//继续下一次自旋
}
h = ThreadLocalRandom.advanceProbe(h);
} else if (cellsBusy == 0 && counterCells == as && UpareAndSwapInt(this, CELLSBUSY, 0, 1)) {
//第一部分:初始化CounterCell数组
//cellsBusy == 0 && UpareAndSwapInt(this, CELLSBUSY, 0, 1),通过cellsBusy字段来抢占锁,通过CAS修改该字段值为1表示抢到锁
boolean init = false;
try {//Initialize table
if (counterCells == as) {
//构造一个元素个数为2的CounterCell数组
CounterCell[] rs = new CounterCell[2];
//把要增加的数组元素个数x,保存到CounterCell数组的某个元素中
rs[h & 1] = new CounterCell(x);
//把初始化的CounterCell数组赋值给全局对象counterCells
counterCells = rs;
init = true;
}
} finally {
//恢复标识
cellsBusy = 0;
}
if (init) {
break;
}
} else if (UpareAndSwapLong(this, BASECOUNT, v = baseCount, v + x)) {
break;//Fall back on using base
}
}
}
...
}
(5)获取数组元素个数的size()方法
sumCount()方法会先得到baseCount的值,保存到sum字段中。然后遍历CounterCell数组,把每个value进行累加。
注意:size()方法在计算总的元素个数时并没有加锁,所以size()方法返回的元素个数不一定代表此时此刻总数量。
代码语言:javascript代码运行次数:0运行复制public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V>, Serializable {
...
//Spinlock (locked via CAS) used when resizing and/or creating CounterCells.
private transient volatile int cellsBusy;
//Table of counter cells. When non-null, size is a power of 2.
private transient volatile CounterCell[] counterCells;
public int size() {
long n = sumCount();
return ((n < 0L) ? 0 : (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)n);
}
final long sumCount() {
CounterCell[] as = counterCells; CounterCell a;
long sum = baseCount;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null) {
sum += a.value;
}
}
}
return sum;
}
...
}
9.ConcurrentHashMap的查询操作是否涉及锁
(1)put操作会加锁
(2)size操作不会加锁
(3)get操作也不会加锁
(1)put操作会加锁
首先尝试通过CAS设置Node数组对应位置的Node元素。如果该位置的Node元素非空,或者CAS设置失败,则说明发生了哈希冲突。此时就会使用synchronized关键字对该数组元素加锁来处理链表或者红黑树。
其实JUC还可以继续优化,先用CAS尝试修改哈希冲突下的链表或红黑树。如果CAS修改失败,再通过使用synchronized对该数组元素加锁来处理。
(2)size操作不会加锁
size()方法在计算总的元素个数时并没有加锁,所以size()方法返回的元素个数不一定代表此时此刻数组元素的总数量。
(3)get操作也不会加锁
get()方法也使用了CAS操作,通过Unsafe类让数组中的元素具有可见性。保证线程根据tabAt()方法获取数组的某个位置的元素时,能获取最新的值。所以get不加锁,但通过volatile读数组,可以保证读到数组元素的最新值。
虽然table变量使用了volatile,但这只保证了table引用对所有线程的可见性,还不能保证table数组中的元素的修改对于所有线程是可见的。因此才通过Unsafe类的getObjectVolatile()来保证table数组中元素的可见性。
代码语言:javascript代码运行次数:0运行复制public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V>, Serializable {
...
//The array of bins. Lazily initialized upon first insertion.
//Size is always a power of two. Accessed directly by iterators.
transient volatile Node<K,V>[] table;
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> 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) {
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;
}
//获取Node数组在位置i的元素,通过Unsafe类让数组中的元素具有可见性
//虽然table变量使用了volatile修饰,但这只保证了table引用对于所有线程的可见性,还不能保证table数组中的元素的修改对于所有线程是可见的
//因此需要通过Unsafe类的getObjectVolatile()来保证table数组中的元素的可见性
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
...
}
10.ConcurrentHashMap中红黑树的使用
(1)treeifyBin()方法的逻辑
(2)TreeBin的成员变量和方法
(3)TreeBin在构造方法中将链表转为红黑树
(4)TreeBin在插入元素时实现红黑树的自平衡
(1)treeifyBin()方法的逻辑
put操作中当发现链表元素>=8时会调用treeifyBin()方法将链表转为红黑树。首先通过tabAt()方法从Node数组中获取位置为index的元素并赋值给变量b,然后使用synchronized对Node数组中位置为index的元素b进行加锁,接着通过for循环遍历Node数组中位置为index的元素b这个链表,并且根据链表中每个结点的数据封装成一个TreeNode对象来组成新链表,最后把新链表的头结点作为参数传给TreeBin构造方法来完成红黑树的构建。
代码语言:javascript代码运行次数:0运行复制public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V>, Serializable {
...
//Replaces all linked nodes in bin at given index unless table is too small, in which case resizes instead.
//将Node数组tab中位置为index的元素,从链表转化为红黑树
private final void treeifyBin(Node<K,V>[] tab, int index) {
Node<K,V> b; int n, sc;
if (tab != null) {
if ((n = tab.length) < MIN_TREEIFY_CAPACITY) {
tryPresize(n << 1);//数组扩容
} else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
synchronized (b) {//b就是链表,先用synchronized对b加锁,保证并发安全
if (tabAt(tab, index) == b) {
TreeNode<K,V> hd = null, tl = null;//hd是新链表的头结点,tl是新链表的尾结点
//将链表b赋值给e,然后遍历通过e.next赋值回给e来遍历链表
for (Node<K,V> e = b; e != null; e = e.next) {
//根据Node结点e来封装一个TreeNode对象
TreeNode<K,V> p = new TreeNode<K,V>(e.hash, e.key, e.val, null, null);
if ((p.prev = tl) == null) {
hd = p;
} else {
//尾插法构造新链表
tl.next = p;
}
tl = p;
}
//将构造好的新链表的头结点hd作为参数,创建一个TreeBin对象
setTabAt(tab, index, new TreeBin<K,V>(hd));
}
}
}
}
}
...
}
(2)TreeBin的成员变量和方法
ConcurrentHashMap中红黑树用继承自Node的TreeNode来表示。TreeBin则主要提供了红黑树的一系列功能的实现,并且实现了读写锁。
代码语言:javascript代码运行次数:0运行复制public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V>, Serializable {
...
//Nodes for use in TreeBins
static final class TreeNode<K,V> extends Node<K,V> {
TreeNode<K,V> parent;//red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev;//needed to unlink next upon deletion
boolean red;
TreeNode(int hash, K key, V val, Node<K,V> next, TreeNode<K,V> parent) {
super(hash, key, val, next);
this.parent = parent;
}
...
}
//TreeNodes used at the heads of bins.
//TreeBins do not hold user keys or values, but instead point to list of TreeNodes and their root.
//They also maintain a parasitic read-write lock forcing writers (who hold bin lock)
//to wait for readers (who do not) to complete before tree restructuring operations.
static final class TreeBin<K,V> extends Node<K,V> {
TreeNode<K,V> root;//红黑树根结点
volatile TreeNode<K,V> first;//链表头结点,由构造函数传入
volatile Thread waiter;//保存最近一个抢占写锁的线程(如果有值,则说明lockState是读锁状态)
volatile int lockState;//表示锁的状态
// values for lockState
static final int WRITER = 1;//写锁状态
static final int WAITER = 2;//等待获取写锁状态
static final int READER = 4;//读锁状态
...
//构造函数,将以b为头结点的链表转换为红黑树
//Creates bin with initial set of nodes headed by b.
TreeBin(TreeNode<K,V> b) {
...
}
//对红黑树的根结点加写锁
//Acquires write lock for tree restructuring.
private final void lockRoot() {
if (!UpareAndSwapInt(this, LOCKSTATE, 0, WRITER)) {
contendedLock(); // offload to separate method
}
}
//释放写锁
//Releases write lock for tree restructuring.
private final void unlockRoot() {
lockState = 0;
}
//根据key获取指定的结点
//Returns matching node or null if none.
//Tries to search using tree comparisons from root, but continues linear search when lock not available.
final Node<K,V> find(int h, Object k) {
...
}
...
}
...
}
(3)TreeBin在构造方法中将链表转为红黑树
treeifyBin()方法在对链表进行转化时,会先构建一个双向链表,然后将该双向链表传入TreeBin的构造方法。
TreeBin的构造方法会通过如下处理将该双向链表转化为红黑树:
一.如果红黑树为空,则初始化红黑树的根结点
二.如果红黑树不为空,则按平衡二叉树逻辑插入
三.通过balanceInsertion()方法进行自平衡
TreeBin的构造方法可以分为三部分:
第一部分:初始化红黑树
遍历链表b,将链表b的头结点设置为红黑树的根结点,接着设置红黑树根结点的左右子结点为null,以及设置红黑树根结点为黑色。
第二部分:将链表中的结点添加到初始化好的红黑树
首先计算dir的值。如果dir = -1,表示红黑树中被插入结点的hash值大于新添加结点x的hash值。如果dir = 1,表示红黑树中被插入结点的hash值小于新添加结点x的hash值。然后根据dir的值来决定新添加结点x是被插入结点的左结点还是右结点,最后调用TreeBin的balanceInsertion()方法对红黑树进行自平衡处理。
第三部分:对红黑树进行自平衡
调用TreeBin的balanceInsertion()方法对红黑树进行自平衡处理。
代码语言:javascript代码运行次数:0运行复制public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V>, Serializable {
...
static final class TreeBin<K,V> extends Node<K,V> {
TreeNode<K,V> root;//红黑树根结点
volatile TreeNode<K,V> first;//链表头结点,由构造函数传入
volatile Thread waiter;//保存最近一个抢占写锁的线程(如果有值,则说明lockState是读锁状态)
volatile int lockState;//表示锁的状态
//values for lockState
static final int WRITER = 1;//写锁状态
static final int WAITER = 2;//等待获取写锁状态
static final int READER = 4;//读锁状态
...
//构造函数,将以b为头结点的链表转换为红黑树
TreeBin(TreeNode<K,V> b) {
//第一部分开始:初始化红黑树
super(TREEBIN, null, null, null);
this.first = b;
//r表示红黑树的根结点
TreeNode<K,V> r = null;
//遍历链表b,x将作为新添加的红黑树结点
for (TreeNode<K,V> x = b, next; x != null; x = next) {
next = (TreeNode<K,V>)x.next;
//把新添加的红黑树结点x的左右子结点设置为null
x.left = x.right = null;
//r表示红黑树的根结点,r == null表示红黑树为空,将x结点设置为红黑树的根结点
if (r == null) {
x.parent = null;
//把红黑树的根结点设置为黑色
x.red = false;
r = x;
//第一部分结束
} else {
//第二部分开始:将链表中的结点添加到初始化好的红黑树中
//x是新添加的红黑树结点
K k = x.key;
int h = x.hash;
Class<?> kc = null;
//p是红黑树中被插入的结点
for (TreeNode<K,V> p = r;;) {
int dir, ph;
K pk = p.key;
//首先计算dir的值
//dir = -1,表示红黑树中被插入结点的hash值大于新添加结点x的hash值
//dir = 1,表示红黑树中被插入结点的hash值小于新添加结点x的hash值
if ((ph = p.hash) > h) {
dir = -1;
} else if (ph < h) {
dir = 1;
} else if ((kc == null && (kc = comparableClassFor(k)) == null) || (dir = compareComparables(kc, k, pk)) == 0) {
dir = tieBreakOrder(k, pk);
TreeNode<K,V> xp = p;
}
//然后根据dir的值来决定新添加的结点x是左结点还是右结点
if ((p = (dir <= 0) ? p.left : p.right) == null) {
x.parent = xp;
if (dir <= 0) {
xp.left = x;
} else {
xp.right = x;
}
//第二部分结束
//第三部分开始:红黑树进行自平衡
//r代表一棵红黑树,x代表往红黑树r添加的结点
r = balanceInsertion(r, x);
//第三部分结束
break;
}
}
}
}
this.root = r;
assert checkInvariants(root);
}
...
}
...
}
(4)TreeBin在插入元素时实现红黑树的自平衡
代码语言:javascript代码运行次数:0运行复制public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V>, Serializable {
...
static final class TreeBin<K,V> extends Node<K,V> {
TreeNode<K,V> root;//红黑树根结点
volatile TreeNode<K,V> first;//链表头结点,由构造函数传入
volatile Thread waiter;//保存最近一个抢占写锁的线程(如果有值,则说明lockState是读锁状态)
volatile int lockState;//表示锁的状态
// values for lockState
static final int WRITER = 1;//写锁状态
static final int WAITER = 2;//等待获取写锁状态
static final int READER = 4;//读锁状态
...
//root代表一棵红黑树,x代表往红黑树r添加的结点
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root, TreeNode<K,V> x) {
//所有往红黑树添加的结点默认为红色
x.red = true;
//自旋,xp表示添加结点x的父结点,xpp表示添加结点x的爷结点,xppl表示爷结点的左结点,xppr表示爷结点的右结点
for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
if ((xp = x.parent) == null) {//此处判断条件表示:x结点的父结点为空
//由于只有根结点的父结点才会为空,所以此时x结点为根结点,于是设置根结点x为黑色
x.red = false;
return x;
} else if (!xp.red || (xpp = xp.parent) == null) {//此处判断条件表示:表示x结点的父结点为黑色,或者x结点的爷结点为空
//那么直接返回红黑树root,不需要处理
return root;
}
//代码执行到这里,说明x结点的父结点为红色
if (xp == (xppl = xpp.left)) {//此处判断条件表示:表示x结点的父结点xp是其爷结点xpp的左子结点xppl
if ((xppr = xpp.right) != null && xppr.red) {//此处判断条件表示:x结点的叔结点存在且为红色
//那么直接修改父结点和叔结点的颜色为黑色
xppr.red = false;
xp.red = false;
xpp.red = true;
x = xpp;
} else {//此处判断条件表示:如果x结点的叔结点不存在,或者叔结点存在且为黑色
if (x == xp.right) {//如果x结点是父结点的右子结点,则按x结点的父结点进行左旋
root = rotateLeft(root, x = xp);//将x结点的父结点赋值给x结点
xpp = (xp = x.parent) == null ? null : xp.parent;
}
if (xp != null) {
xp.red = false;
if (xpp != null) {
xpp.red = true;
root = rotateRight(root, xpp);
}
}
}
} else {//表示x结点的父结点是其爷结点的右子结点
if (xppl != null && xppl.red) {
xppl.red = false;
xp.red = false;
xpp.red = true;
x = xpp;
} else {
if (x == xp.left) {
root = rotateRight(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
if (xp != null) {
xp.red = false;
if (xpp != null) {
xpp.red = true;
root = rotateLeft(root, xpp);
}
}
}
}
}
}
static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root, TreeNode<K,V> p) {
TreeNode<K,V> r, pp, rl;
if (p != null && (r = p.right) != null) {
if ((rl = p.right = r.left) != null) {
rl.parent = p;
}
if ((pp = r.parent = p.parent) == null) {
(root = r).red = false;
} else if (pp.left == p) {
pp.left = r;
} else {
pp.right = r;
}
r.left = p;
p.parent = r;
}
return root;
}
static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root, TreeNode<K,V> p) {
TreeNode<K,V> l, pp, lr;
if (p != null && (l = p.left) != null) {
if ((lr = p.left = l.right) != null) {
lr.parent = p;
}
if ((pp = l.parent = p.parent) == null) {
(root = l).red = false;
} else if (pp.right == p) {
pp.right = l;
} else {
pp.left = l;
}
l.right = p;
p.parent = l;
}
return root;
}
...
}
}
本文标签: JUC并发8并发安全集合二
版权声明:本文标题:JUC并发—8.并发安全集合二 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/biancheng/1747490419a2699791.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论