国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

JAVA中的偽共享與緩存行

這篇具有很好參考價值的文章主要介紹了JAVA中的偽共享與緩存行。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

一.偽共享與緩存行

1.CPU緩存架構(gòu)

CPU 是計算機的心臟,所有運算和程序最終都要由它來執(zhí)行。

主內(nèi)存(RAM)是數(shù)據(jù)存放的地方,CPU 和主內(nèi)存之間有好幾級緩存,因為即使直接訪問主內(nèi)存也是非常慢的。

CPU的速度要遠遠大于內(nèi)存的速度,為了解決這個問題,CPU引入了三級緩存:L1,L2和L3三個級別,L1最靠近CPU,L2次之,L3離CPU最遠,L3之后才是主存。速度是L1>L2>L3>主存。越靠近CPU的容量越小。CPU獲取數(shù)據(jù)會依次從三級緩存中查找

java 偽共享,java,java

當(dāng)CPU要讀取一個數(shù)據(jù)時,首先從一級緩存中查找,如果沒有找到再從二級緩存中查找,如果還是沒有就從三級緩存或內(nèi)存中查找。一般來說,每級緩存的命中率大概都在80%左右,也就是說全部數(shù)據(jù)量的80%都可以在一級緩存中找到,只剩下20%的總數(shù)據(jù)量才需要從二級緩存、三級緩存或內(nèi)存中讀取,由此可見一級緩存是整個CPU緩存架構(gòu)中最為重要的部分。

java 偽共享,java,java

?2.什么是偽共享

計算機系統(tǒng)中為了解決主內(nèi)存與CPU運行速度的差距,在CPU與主內(nèi)存之間添加了一級或者多級高速緩沖存儲器(Cache),這個Cache一般是集成到CPU內(nèi)部的,所以也叫 CPU Cache,如下圖是兩級cache結(jié)構(gòu)

java 偽共享,java,java

Cache內(nèi)部是按行存儲的,其中每一行稱為一個緩存行,緩存行是Cache與主內(nèi)存進行數(shù)據(jù)交換的單位,緩存行的大小一般為2的冪次數(shù)字節(jié)。?

當(dāng)CPU訪問某一個變量時候,首先會去看CPU Cache內(nèi)是否有該變量,如果有則直接從中獲取,否者就去主內(nèi)存里面獲取該變量,然后把該變量所在內(nèi)存區(qū)域的一個Cache行大小的內(nèi)存拷貝到Cache(cache行是Cache與主內(nèi)存進行數(shù)據(jù)交換的單位)。由于存放到Cache行的的是內(nèi)存塊而不是單個變量,所以可能會把多個變量存放到了一個cache行。當(dāng)多個線程同時修改一個緩存行里面的多個變量時候,由于同時只能有一個線程操作緩存行,所以相比每個變量放到一個緩存行性能會有所下降,這就是偽共享。

java 偽共享,java,java

?文章來源地址http://www.zghlxwxcb.cn/news/detail-518334.html

?如上圖變量x,y同時被放到了CPU的一級和二級緩存,當(dāng)線程1使用CPU1對變量x進行更新時候,首先會修改cpu1的一級緩存變量x所在緩存行,這時候緩存一致性協(xié)議會導(dǎo)致cpu2中變量x對應(yīng)的緩存行失效,那么線程2寫入變量x的時候就只能去二級緩存去查找,這就破壞了一級緩存,而一級緩存比二級緩存更快。更壞的情況下如果cpu只有一級緩存,那么會導(dǎo)致頻繁的直接訪問主內(nèi)存。

3.為何會出現(xiàn)偽共享

? 偽共享的產(chǎn)生是因為多個變量被放入了一個緩存行,并且多個線程同時去寫入緩存行中不同變量。那么為何多個變量會被放入一個緩存行那。其實是因為Cache與內(nèi)存交換數(shù)據(jù)的單位就是Cache,當(dāng)CPU要訪問的變量沒有在Cache命中時候,根據(jù)程序運行的局部性原理會把該變量在內(nèi)存中大小為Cache行的內(nèi)存放如緩存行。

4.Java中的偽共享

?解決偽共享最直接的方法就是填充(padding),例如下面的VolatileLong,一個long占8個字節(jié),Java的對象頭占用8個字節(jié)(32位系統(tǒng))或者12字節(jié)(64位系統(tǒng),默認(rèn)開啟對象頭壓縮,不開啟占16字節(jié))。一個緩存行64字節(jié),那么我們可以填充6個long(6 * 8 = 48 個字節(jié))。

? 現(xiàn)在,我們學(xué)習(xí)JVM對象的內(nèi)存模型。所有的Java對象都有8字節(jié)的對象頭,前四個字節(jié)用來保存對象的哈希碼和鎖的狀態(tài),前3個字節(jié)用來存儲哈希碼,最后一個字節(jié)用來存儲鎖狀態(tài),一旦對象上鎖,這4個字節(jié)都會被拿出對象外,并用指針進行鏈接。剩下4個字節(jié)用來存儲對象所屬類的引用。對于數(shù)組來講,還有一個保存數(shù)組大小的變量,為4字節(jié)。每一個對象的大小都會對齊到8字節(jié)的倍數(shù),不夠8字節(jié)部分需要填充。為了保證效率,Java編譯器在編譯Java對象的時候,通過字段類型對Java對象的字段進行排序,如下表所示。

java 偽共享,java,java

?? 因此,我們可以在任何字段之間通過填充長整型的變量把熱點變量隔離在不同的緩存行中,通過減少偽同步,在多核心CPU中能夠極大的提高效率。

最簡單的方式
/**
 * 緩存行填充父類
 */
public class DataPadding {
    //填充 6個long類型字段 8*4 = 48 個字節(jié)
    private long p1, p2, p3, p4, p5, p6;
    //需要操作的數(shù)據(jù)
    private long data;
}

因為JDK1.7以后就自動優(yōu)化代碼會刪除無用的代碼,在JDK1.7以后的版本這些不生效了

繼承的方式
/**
 * 緩存行填充父類
 */
public class DataPadding {
    //填充 6個long類型字段 8*4 = 48 個字節(jié)
    private long p1, p2, p3, p4, p5, p6;
}

繼承緩存填充類

/**
 * 繼承DataPadding
 */
public class VolatileData extends DataPadding {
    // 占用 8個字節(jié) +48 + 對象頭 = 64字節(jié)
    private long data = 0;

    public VolatileData() {
    }

    public VolatileData(long defValue) {
        this.data = defValue;
    }

    public long accumulationAdd() {
          //因為單線程操作不需要加鎖
         data++;
        return data;
    }

    public long getValue() {
        return data;
    }
}

這樣在JDK1.8中是可以使用的

@Contended注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Contended {
    String value() default "";
}

Contended注解可以用于類型上和屬性上,加上這個注解之后虛擬機會自動進行填充,從而避免偽共享。這個注解在Java8 ConcurrentHashMap、ForkJoinPool和Thread等類中都有應(yīng)用。我們來看一下Java8中ConcurrentHashMap中如何運用Contended這個注解來解決偽共享問題。以下說的ConcurrentHashMap都是Java8版本。

注意:在Java8中提供了**@sun.misc.Contended來避免偽共享時,在運行時需要設(shè)置JVM啟動參數(shù)-XX:-RestrictContended**否則可能不生效。

緩存行填充的威力


/**
 * 緩存行測試
 */
public class CacheLineTest {
    /**
     * 是否啟用緩存行填充
     */
    private final boolean isDataPadding = false;
    /**
     * 正常定義的變量
     */
    private volatile long x = 0;
    private volatile long y = 0;
    private volatile long z = 0;
    /**
     * 通過緩存行填充的變量
     */
    private volatile VolatileData volatileDataX = new VolatileData(0);
    private volatile VolatileData volatileDataY = new VolatileData(0);
    private volatile VolatileData volatileDataZ = new VolatileData(0);

    /**
     * 循環(huán)次數(shù)
     */
    private final long size = 100000000;

    /**
     * 進行累加操作
     */
    public void accumulationX() {
        //計算耗時
        long currentTime = System.currentTimeMillis();
        long value = 0;
        //循環(huán)累加
        for (int i = 0; i < size; i++) {
            //使用緩存行填充的方式
            if (isDataPadding) {
                value = volatileDataX.accumulationAdd();
            } else {
                //不使用緩存行填充的方式 因為時單線程操作不需要加鎖
                value = (++x);
            }


        }
        //打印
        System.out.println(value);
        //打印耗時
        System.out.println("耗時:" + (System.currentTimeMillis() - currentTime));
    }

    /**
     * 進行累加操作
     */
    public void accumulationY() {
        long currentTime = System.currentTimeMillis();
        long value = 0;
        for (int i = 0; i < size; i++) {
            if (isDataPadding) {
                value = volatileDataY.accumulationAdd();
            } else {
                value = ++y;
            }


        }
        System.out.println(value);
        System.out.println("耗時:" + (System.currentTimeMillis() - currentTime));
    }

    /**
     * 進行累加操作
     */
    public void accumulationZ() {
        long currentTime = System.currentTimeMillis();
        long value = 0;
        for (int i = 0; i < size; i++) {
            if (isDataPadding) {
                value = volatileDataZ.accumulationAdd();
            } else {
                value = ++z;
            }
        }
        System.out.println(value);
        System.out.println("耗時:" + (System.currentTimeMillis() - currentTime));
    }

    public static void main(String[] args) {
        //創(chuàng)建對象
        CacheLineTest cacheRowTest = new CacheLineTest();
        //創(chuàng)建線程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        //啟動三個線程個調(diào)用他們各自的方法
        executorService.execute(() -> cacheRowTest.accumulationX());
        executorService.execute(() -> cacheRowTest.accumulationY());
        executorService.execute(() -> cacheRowTest.accumulationZ());
        executorService.shutdown();
    }
}
不使用緩存行填充測試
/**
  * 是否啟用緩存行填充
  */
 private final boolean isDataPadding = false;

輸出

100000000
耗時:7960
100000000
耗時:7984
100000000
耗時:7989
使用緩存行填充測試
/**
  * 是否啟用緩存行填充
  */
 private final boolean isDataPadding = true;

輸出

100000000
耗時:176
100000000
耗時:178
100000000
耗時:182

同樣的結(jié)構(gòu)他們之間差了 將近 50倍的速度差距

總結(jié)

? 當(dāng)多個線程同時對共享的緩存行進行寫操作的時候,因為緩存系統(tǒng)自身的緩存一致性原則,會引發(fā)偽共享問題,解決的常用辦法是將共享變量根據(jù)緩存行大小進行補充對齊,使其加載到緩存時能夠獨享緩存行,避免與其他共享變量存儲在同一個緩存行。

?

到了這里,關(guān)于JAVA中的偽共享與緩存行的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 【Android】細數(shù)Linux和Android系統(tǒng)中的偽文件系統(tǒng)

    【Android】細數(shù)Linux和Android系統(tǒng)中的偽文件系統(tǒng)

    做了好些年Android開發(fā),你了解過Linux偽文件系統(tǒng)嗎? 在 Linux 中,偽文件系統(tǒng)(Pseudo Filesystem)是一種特殊的文件系統(tǒng),它不涉及具體的物理存儲設(shè)備,而是提供了一種接口,允許用戶和應(yīng)用程序以文件和目錄的形式訪問內(nèi)核和其他系統(tǒng)信息。這些文件和目錄在運行時被內(nèi)核動

    2024年01月24日
    瀏覽(26)
  • JavaScript前端中的偽類元素before和after使用詳解

    JavaScript前端中的偽類元素before和after使用詳解

    在前端開發(fā)中,偽類是一種讓你可以選擇元素的某個狀態(tài)或位置的 CSS 選擇器。其中, :before 和 :after 偽類允許你在一個元素之前或之后插入內(nèi)容。 :before 和 :after 偽類創(chuàng)建的元素是不在 HTML 文檔中的,它們是通過 CSS 生成的。可以用它們來在一個元素的前面或后面插入一些內(nèi)

    2024年02月14日
    瀏覽(20)
  • 合肥工業(yè)大學(xué)機器視覺期末復(fù)習(xí) 課件梳理(穿插作業(yè)中的偽代碼)

    合肥工業(yè)大學(xué)機器視覺期末復(fù)習(xí) 課件梳理(穿插作業(yè)中的偽代碼)

    第一部分:低層次視覺 1、濾波器 2、梯度—邊緣;梯度—能量(線裁剪) 3、模板匹配;二值圖像分析 4、紋理 第二部分:中層次視覺 5、霍夫變換 6、分割 7、局部不變特征——檢測、描述和匹配 8、立體 第三部分:高層次的視覺 9、實例識別 10、監(jiān)督分類的對象檢測 11、支持向

    2024年02月02日
    瀏覽(27)
  • 【java緩存、redis緩存、guava緩存】java中實現(xiàn)緩存的幾種方式

    【java緩存、redis緩存、guava緩存】java中實現(xiàn)緩存的幾種方式

    這種方式可以簡單實現(xiàn)本地緩存,但是實際開發(fā)中不推薦使用,下面我們來實現(xiàn)一下這種方式。 首先創(chuàng)建一個管理緩存的類 這個類中有一個靜態(tài)代碼塊,靜態(tài)代碼塊會在類加載時就執(zhí)行,我們可以在這里完成對緩存的初始化,決定緩存內(nèi)一開始就有哪些數(shù)據(jù) 另外我們還可以

    2024年02月16日
    瀏覽(15)
  • Java擴展Nginx之七:共享內(nèi)存

    Java擴展Nginx之七:共享內(nèi)存

    這里分類和匯總了欣宸的全部原創(chuàng)(含配套源碼):https://github.com/zq2599/blog_demos 作為《Java擴展Nginx》系列的第七篇,咱們來了解一個實用工具 共享內(nèi)存 ,正式開始之前先來看一個問題 在一臺電腦上,nginx開啟了多個worker,如下圖,如果此時我們用了nginx-clojure,就相當(dāng)于有了四

    2024年02月16日
    瀏覽(22)
  • 【Java】線程數(shù)據(jù)共享和安全 -ThreadLocal

    【Java】線程數(shù)據(jù)共享和安全 -ThreadLocal

    ???歡迎來到@邊境矢夢°的csdn博文?? ???本文主要梳理線程數(shù)據(jù)共享和安全 -ThreadLocal?? ??我是邊境矢夢°,一個正在為秋招和算法競賽做準(zhǔn)備的學(xué)生?? ??喜歡的朋友可以關(guān)注一下 ?????? ,下次更新不迷路?? Ps: 月亮越亮說明知識點越重要 (重要性或者難度越大)????

    2024年02月09日
    瀏覽(44)
  • 【Java 進階篇】Java Cookie共享:讓數(shù)據(jù)穿越不同應(yīng)用的時空隧道

    【Java 進階篇】Java Cookie共享:讓數(shù)據(jù)穿越不同應(yīng)用的時空隧道

    在Web開發(fā)中,Cookie是一種常見的會話管理技術(shù),用于存儲和傳遞用戶相關(guān)的信息。通常,每個Web應(yīng)用都會在用戶的瀏覽器中設(shè)置自己的Cookie,以便在用戶與應(yīng)用之間保持狀態(tài)。然而,有時我們需要在不同的應(yīng)用之間共享Cookie數(shù)據(jù),讓數(shù)據(jù)像穿越時空的時光旅行一樣在不同的

    2024年02月05日
    瀏覽(22)
  • 【Java 基礎(chǔ)篇】Java網(wǎng)絡(luò)編程實戰(zhàn):P2P文件共享詳解

    【Java 基礎(chǔ)篇】Java網(wǎng)絡(luò)編程實戰(zhàn):P2P文件共享詳解

    Java網(wǎng)絡(luò)編程是現(xiàn)代軟件開發(fā)中不可或缺的一部分,因為它允許不同計算機之間的數(shù)據(jù)傳輸和通信。在本篇博客中,我們將深入探討Java中的P2P文件共享,包括什么是P2P文件共享、如何實現(xiàn)它以及一些相關(guān)的重要概念。 P2P(Peer-to-Peer)文件共享是一種分布式計算模型,其中每個

    2024年02月07日
    瀏覽(26)
  • 【開源】基于JAVA的教學(xué)資源共享平臺

    【開源】基于JAVA的教學(xué)資源共享平臺

    基于JAVA+Vue+SpringBoot+MySQL的教學(xué)資源共享平臺,包含了課程管理、課程課件、授課中心、作業(yè)發(fā)布、課程評價、課程質(zhì)量分析、交流互動模塊,還包含系統(tǒng)自帶的用戶管理、部門管理、角色管理、菜單管理、日志管理、數(shù)據(jù)字典管理、文件管理、圖表展示等基礎(chǔ)模塊,教學(xué)資源

    2024年01月19日
    瀏覽(27)
  • 【開源】基于JAVA語言的教學(xué)資源共享平臺

    【開源】基于JAVA語言的教學(xué)資源共享平臺

    基于JAVA+Vue+SpringBoot+MySQL的教學(xué)資源共享平臺,包含了課程管理、課程課件、授課中心、作業(yè)發(fā)布、課程評價、課程質(zhì)量分析、交流互動模塊,還包含系統(tǒng)自帶的用戶管理、部門管理、角色管理、菜單管理、日志管理、數(shù)據(jù)字典管理、文件管理、圖表展示等基礎(chǔ)模塊,教學(xué)資源

    2024年01月20日
    瀏覽(33)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包