這篇文章主要介紹“Java并發編程之LongAdder執行情況是什么”,在日常操作中,相信很多人在Java并發編程之LongAdder執行情況是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Java并發編程之LongAdder執行情況是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
final void longAccumulate(long x, LongBinaryOperator fn,
boolean wasUncontended) {
int h;
if ((h = getProbe()) == 0) {
ThreadLocalRandom.current(); // force initialization
h = getProbe();
wasUncontended = true;
}
boolean collide = false; // True if last slot nonempty
for (;;) {
Cell[] as; Cell a; int n; long v;
if ((as = cells) != null && (n = as.length) > 0) {
if ((a = as[(n - 1) & h]) == null) {
if (cellsBusy == 0) { // Try to attach new Cell
Cell r = new Cell(x); // Optimistically create
if (cellsBusy == 0 && casCellsBusy()) {
boolean created = false;
try { // Recheck under lock
Cell[] rs; int m, j;
if ((rs = cells) != null &&
(m = rs.length) > 0 &&
rs[j = (m - 1) & h] == null) {
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 (a.cas(v = a.value, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break;
else if (n >= NCPU || cells != as)
collide = false; // At max size or stale
else if (!collide)
collide = true;
else if (cellsBusy == 0 && casCellsBusy()) {
try {
if (cells == as) { // Expand table unless stale
Cell[] rs = new Cell[n << 1];
for (int i = 0; i < n; ++i)
rs[i] = as[i];
cells = rs;
}
} finally {
cellsBusy = 0;
}
collide = false;
continue; // Retry with expanded table
}
h = advanceProbe(h);
}
else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
boolean init = false;
try { // Initialize table
if (cells == as) {
Cell[] rs = new Cell[2];
rs[h & 1] = new Cell(x);
cells = rs;
init = true;
}
} finally {
cellsBusy = 0;
}
if (init)
break;
}
else if (casBase(v = base, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break; // Fall back on using base
}
}代碼較長,我們分段來分析,首先介紹一下各部分的內容
第一部分:for循環之前的代碼,主要是獲取線程的hash值,如果是0的話就強制初始化
第二部分:for循環中第一個if語句,在Cell數組中進行累加、擴容
第三部分:for循環中第一個else if語句,這部分的作用是創建Cell數組并初始化
第四部分:for循環中第二個else if語句,當Cell數組競爭激烈時嘗試在base上進行累加
int h;
if ((h = getProbe()) == 0) {
ThreadLocalRandom.current(); // force initialization
h = getProbe();
wasUncontended = true; // true表示沒有競爭
}
boolean collide = false; // True if last slot nonempty 可以理解為是否需要擴容這部分的核心代碼是getProbe方法,這個方法的作用就是獲取線程的hash值,方便后面通過位運算定位到Cell數組中某個位置,如果是0的話就會進行強制初始化
final void longAccumulate(long x, LongBinaryOperator fn,
boolean wasUncontended) {
// 省略...
for (;;) {
Cell[] as; Cell a; int n; long v;
if ((as = cells) != null && (n = as.length) > 0) {
// 省略...
}
else if (cellsBusy == 0 && cells == as && casCellsBusy()) { // 獲取鎖
boolean init = false; // 初始化標志
try { // Initialize table
if (cells == as) {
Cell[] rs = new Cell[2]; // 創建Cell數組
rs[h & 1] = new Cell(x); // 索引1位置創建Cell元素,值為x=1
cells = rs; // cells指向新數組
init = true; // 初始化完成
}
} finally {
cellsBusy = 0; // 釋放鎖
}
if (init)
break; // 跳出循環
}
else if (casBase(v = base, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break; // Fall back on using base
}
}第一種情況下Cell數組為null,所以會進入第一個else if語句,并且沒有其他線程進行操作,所以cellsBusy==0,cells==as也是true,casCellsBusy()嘗試對cellsBusy進行cas操作改成1也是true。
首先創建了一個有兩個元素的Cell數組,然后通過線程h & 1 的位運算在索引1的位置設置一個value為1的Cell,然后重新賦值給cells,標記初始化成功,修改cellsBusy為0表示釋放鎖,最后跳出循環,初始化操作就完成了。
final void longAccumulate(long x, LongBinaryOperator fn,
boolean wasUncontended) {
// 省略...
for (;;) {
Cell[] as; Cell a; int n; long v;
if ((as = cells) != null && (n = as.length) > 0) {
// 省略...
}
else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
// 省略...
}
else if (casBase(v = base, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break; // Fall back on using base
}
}第二個else if語句的意思是當Cell數組中所有位置競爭都很激烈時,就嘗試在base上進行累加,可以理解為最后的保障
final void longAccumulate(long x, LongBinaryOperator fn,
boolean wasUncontended) {
// 省略...
for (;;) {
Cell[] as; Cell a; int n; long v;
if ((as = cells) != null && (n = as.length) > 0) { // as初始化之后滿足條件
if ((a = as[(n - 1) & h]) == null) { // as中某個位置的值為null
if (cellsBusy == 0) { // Try to attach new Cell 是否加鎖
Cell r = new Cell(x); // Optimistically create 創建新Cell
if (cellsBusy == 0 && casCellsBusy()) { // 雙重檢查是否有鎖,并嘗試加鎖
boolean created = false; //
try { // Recheck under lock
Cell[] rs; int m, j;
if ((rs = cells) != null &&
(m = rs.length) > 0 &&
rs[j = (m - 1) & h] == null) { // 重新檢查該位置是否為null
rs[j] = r; // 該位置添加Cell元素
created = true; // 新Cell創建成功
}
} finally {
cellsBusy = 0; // 釋放鎖
}
if (created)
break; // 創建成功,跳出循環
continue; // Slot is now non-empty
}
}
collide = false; // 擴容標志
}
else if (!wasUncontended) // 上面定位到的索引位置的值不為null
wasUncontended = true; // 重新計算hash,重新定位其他索引位置重試
else if (a.cas(v = a.value, ((fn == null) ? v + x :
fn.applyAsLong(v, x)))) // 嘗試在該索引位置進行累加
break;
else if (n >= NCPU || cells != as) // 如果數組長度大于等于CPU核心數,就不能在擴容
collide = false; // At max size or stale
else if (!collide) // 數組長度沒有達到最大值,修改擴容標志可以擴容
collide = true;
else if (cellsBusy == 0 && casCellsBusy()) { // 嘗試加鎖
try {
if (cells == as) { // Expand table unless stale
Cell[] rs = new Cell[n << 1]; // 創建一個原來長度2倍的數組
for (int i = 0; i < n; ++i)
rs[i] = as[i]; // 把原來的元素拷貝到新數組中
cells = rs; // cells指向新數組
}
} finally {
cellsBusy = 0; // 釋放鎖
}
collide = false; // 已經擴容完成,修改標志不用再擴容
continue; // Retry with expanded table
}
h = advanceProbe(h); // 重新獲取hash值
}
// 省略...
}根據代碼中的注釋分析一遍整體邏輯
首先如果找到數組某個位置上的值為null,說明可以在這個位置進行操作,就創建一個新的Cell并初始化值為1放到這個位置,如果失敗了就重新計算hash值再重試
定位到的位置已經有值了,說明線程之間產生了競爭,如果wasUncontended是false就修改為true,并重新計算hash重試
定位的位置有值并且wasUncontended已經是true,就嘗試在該位置進行累加
當累加失敗時,判斷數組容量是否已經達到最大,如果是就不能進行擴容,只能rehash并重試
如果前面條件都不滿足,并且擴容標志collide標記為false的話就修改為true,表示可以進行擴容,然后rehash重試
首先嘗試加鎖,成功了就進行擴容操作,每次擴容長度是之前的2倍,然后把原來數組內容拷貝到新數組,擴容就完成了。
到此,關于“Java并發編程之LongAdder執行情況是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。