日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

java中什么是 伪共享_【Java】聊聊多线程中的伪共享现象

發布時間:2025/3/12 java 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java中什么是 伪共享_【Java】聊聊多线程中的伪共享现象 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首頁

專欄

java

文章詳情

0

聊聊多線程中的偽共享現象

小強大人發布于 1 月 27 日

什么是偽共享?

講偽共享之前,讓我們先乘坐時光機,回到大學課堂,來重溫下計算機組成原理的基礎知識。我們知道,CPU和內存的運行速度相差很大,為了解決這個問題,在CPU和內存之間會加一級或多級高速緩存(Cache)。這個Cache一般是集成在CPU內部的,所以也叫CPU Cache。下圖是一個兩級Cache的CPU-Cache-內存架構。

數據在Cache中是按行存儲的,其中每一行稱為一個Cache行,如下圖所示。它是CPU與內存數據交換的基本單位。Cache行的大小一般為2的冪次字節數。

CPU-Cache-內存架構的工作原理是這樣的:當CPU訪問某個變量時,首先會從CPU Cache里查看是否有該變量,如果有直接獲取并返回,否則從內存中獲取,然后把該變量所在內存區域的一個Cache行大小的內存數據復制到Cache中,這就是我們所說的局部性原理。由于存放到Cache行的不是單個變量,而是一個內存塊數據,所以會出現多個變量放在一個Cache行中。當多個線程同時修改同一個緩存行里面的不同變量時,由于同一時刻只允許一個線程操作緩存行,一個線程成功獲取緩存行的修改權時,其他線程會互斥等待,并且由于緩存一致性協議,其他線程相應的緩存行會失效,需要重新從內存獲取數據,這無疑耗費了更多時間。所以相比把每個變量放在不同的緩存行,性能反而有所下降,這就是偽共享現象。

如下圖所示,變量x,y放在同一個緩存行中,線程1操作緩存行的x變量,線程2操作y變量。

如何避免偽共享呢?

在JDK1.8之前,通常是通過字節填充的方式。什么意思呢?就是用到一個變量時,補充額外的若干輔助變量,使得這些變量剛好填充滿一個緩存行,這樣就避免了多個變量存放在一個緩存行中。具體看下面示例代碼:

static final class PaddedLongField {

public volatile long value = 0L;

public long p1,p2,p3,p4,p5,p6;

}

假如CPU Cache行大小為64字節,那么我們這里填充了6個long型的變量,每個long型變量占8個字節,加上value一共7*8=56字節,另外,別忘了PaddedLongField是一個類對象,對象頭還要占用8個字節,所以一個PaddedLongField對象占用64個字節,剛好填充滿一個緩存行。

在JDK1.8之后,提供了一個@sun.misc.Contended注解,用來解決偽共享問題。此時,我們上面的代碼就可以簡化了:

@sun.misc.Contended

static final class PaddedLongField {

public volatile long value = 0L;

}

@Contended注解不僅可以修飾類,也可以修飾變量:

JUC源碼里很多使用這個注解的,比如Thread類里threadLocalRandom相關的變量:

再比如LongAdder內部用到的Cell也用了這個注解:

再比如ForkJoinPool類上面也修飾了:

最后需要注意下,@Contended注解默認只用于Java核心類,比如rt.jar下的類。如果我們應用程序中想使用這個注解,需要添加一個JVM參數:-XX:-RestrictContended,填充的默認寬度為128字節,若需自定義寬度則可以用另一個參數:-XX:ContendedPaddingWidth=xxx

本文就到這里啦,若這篇文章對你有所幫助的話,點個贊再走叭!謝謝支持!

參考資料:

《Java并發編程之美》

java

閱讀 53更新于 1 月 27 日

贊收藏

分享

本作品系原創,采用《署名-非商業性使用-禁止演繹 4.0 國際》許可協議

小強大人

9聲望

1粉絲

關注作者

0 條評論

得票時間

提交評論

小強大人

9聲望

1粉絲

關注作者

宣傳欄

什么是偽共享?

講偽共享之前,讓我們先乘坐時光機,回到大學課堂,來重溫下計算機組成原理的基礎知識。我們知道,CPU和內存的運行速度相差很大,為了解決這個問題,在CPU和內存之間會加一級或多級高速緩存(Cache)。這個Cache一般是集成在CPU內部的,所以也叫CPU Cache。下圖是一個兩級Cache的CPU-Cache-內存架構。

數據在Cache中是按行存儲的,其中每一行稱為一個Cache行,如下圖所示。它是CPU與內存數據交換的基本單位。Cache行的大小一般為2的冪次字節數。

CPU-Cache-內存架構的工作原理是這樣的:當CPU訪問某個變量時,首先會從CPU Cache里查看是否有該變量,如果有直接獲取并返回,否則從內存中獲取,然后把該變量所在內存區域的一個Cache行大小的內存數據復制到Cache中,這就是我們所說的局部性原理。由于存放到Cache行的不是單個變量,而是一個內存塊數據,所以會出現多個變量放在一個Cache行中。當多個線程同時修改同一個緩存行里面的不同變量時,由于同一時刻只允許一個線程操作緩存行,一個線程成功獲取緩存行的修改權時,其他線程會互斥等待,并且由于緩存一致性協議,其他線程相應的緩存行會失效,需要重新從內存獲取數據,這無疑耗費了更多時間。所以相比把每個變量放在不同的緩存行,性能反而有所下降,這就是偽共享現象。

如下圖所示,變量x,y放在同一個緩存行中,線程1操作緩存行的x變量,線程2操作y變量。

如何避免偽共享呢?

在JDK1.8之前,通常是通過字節填充的方式。什么意思呢?就是用到一個變量時,補充額外的若干輔助變量,使得這些變量剛好填充滿一個緩存行,這樣就避免了多個變量存放在一個緩存行中。具體看下面示例代碼:

static final class PaddedLongField {

public volatile long value = 0L;

public long p1,p2,p3,p4,p5,p6;

}

假如CPU Cache行大小為64字節,那么我們這里填充了6個long型的變量,每個long型變量占8個字節,加上value一共7*8=56字節,另外,別忘了PaddedLongField是一個類對象,對象頭還要占用8個字節,所以一個PaddedLongField對象占用64個字節,剛好填充滿一個緩存行。

在JDK1.8之后,提供了一個@sun.misc.Contended注解,用來解決偽共享問題。此時,我們上面的代碼就可以簡化了:

@sun.misc.Contended

static final class PaddedLongField {

public volatile long value = 0L;

}

@Contended注解不僅可以修飾類,也可以修飾變量:

JUC源碼里很多使用這個注解的,比如Thread類里threadLocalRandom相關的變量:

再比如LongAdder內部用到的Cell也用了這個注解:

再比如ForkJoinPool類上面也修飾了:

最后需要注意下,@Contended注解默認只用于Java核心類,比如rt.jar下的類。如果我們應用程序中想使用這個注解,需要添加一個JVM參數:-XX:-RestrictContended,填充的默認寬度為128字節,若需自定義寬度則可以用另一個參數:-XX:ContendedPaddingWidth=xxx

本文就到這里啦,若這篇文章對你有所幫助的話,點個贊再走叭!謝謝支持!

參考資料:

《Java并發編程之美》

總結

以上是生活随笔為你收集整理的java中什么是 伪共享_【Java】聊聊多线程中的伪共享现象的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。