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

【javaEE面試題(四)線程不安全的原因】【1. 修改共享數(shù)據(jù) 2. 操作不是原子性 3. 內(nèi)存可見性 4. 代碼順序性】

這篇具有很好參考價(jià)值的文章主要介紹了【javaEE面試題(四)線程不安全的原因】【1. 修改共享數(shù)據(jù) 2. 操作不是原子性 3. 內(nèi)存可見性 4. 代碼順序性】。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

4. 多線程帶來的的風(fēng)險(xiǎn)-線程安全 (重點(diǎn))

【javaEE面試題(四)線程不安全的原因】【1. 修改共享數(shù)據(jù) 2. 操作不是原子性 3. 內(nèi)存可見性 4. 代碼順序性】,面試題,JavaEE初階,java-ee,安全,java

4.1 觀察線程不安全

static class Counter {
    public int count = 0;
    void increase() {
        count++;
   }
}
public static void main(String[] args) throws InterruptedException {
    final Counter counter = new Counter();
    Thread t1 = new Thread(() -> {
        for (int i = 0; i < 50000; i++) {
            counter.increase();
       }
   });
    Thread t2 = new Thread(() -> {
        for (int i = 0; i < 50000; i++) {
            counter.increase();
       }
   });
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(counter.count);
}

大家觀察下是否適用多線程的現(xiàn)象是否一致?同時(shí)嘗試思考下為什么會(huì)有這樣的現(xiàn)象發(fā)生呢?

原因是 1.load 2. add 3. save

【javaEE面試題(四)線程不安全的原因】【1. 修改共享數(shù)據(jù) 2. 操作不是原子性 3. 內(nèi)存可見性 4. 代碼順序性】,面試題,JavaEE初階,java-ee,安全,java
【javaEE面試題(四)線程不安全的原因】【1. 修改共享數(shù)據(jù) 2. 操作不是原子性 3. 內(nèi)存可見性 4. 代碼順序性】,面試題,JavaEE初階,java-ee,安全,java【javaEE面試題(四)線程不安全的原因】【1. 修改共享數(shù)據(jù) 2. 操作不是原子性 3. 內(nèi)存可見性 4. 代碼順序性】,面試題,JavaEE初階,java-ee,安全,java
【javaEE面試題(四)線程不安全的原因】【1. 修改共享數(shù)據(jù) 2. 操作不是原子性 3. 內(nèi)存可見性 4. 代碼順序性】,面試題,JavaEE初階,java-ee,安全,java
【javaEE面試題(四)線程不安全的原因】【1. 修改共享數(shù)據(jù) 2. 操作不是原子性 3. 內(nèi)存可見性 4. 代碼順序性】,面試題,JavaEE初階,java-ee,安全,java
注意:可能會(huì)導(dǎo)致 小于5w

4.2 線程安全的概念

想給出一個(gè)線程安全的確切定義是復(fù)雜的,但我們可以這樣認(rèn)為:
如果多線程環(huán)境下代碼運(yùn)行的結(jié)果是符合我們預(yù)期的,即在單線程環(huán)境應(yīng)該的結(jié)果,則說這個(gè)程序是線程安全的

4.3 線程不安全的原因

★1. 修改共享數(shù)據(jù)(多個(gè)線程修改同一個(gè)變量)

上面的線程不安全的代碼中, 涉及到多個(gè)線程針對(duì) counter.count 變量進(jìn)行修改.
此時(shí)這個(gè) counter.count 是一個(gè)多個(gè)線程都能訪問到的 “共享數(shù)據(jù)”【javaEE面試題(四)線程不安全的原因】【1. 修改共享數(shù)據(jù) 2. 操作不是原子性 3. 內(nèi)存可見性 4. 代碼順序性】,面試題,JavaEE初階,java-ee,安全,java
counter.count 這個(gè)變量就是在堆上. 因此可以被多個(gè)線程共享訪問

★2. 操作不是原子性

【javaEE面試題(四)線程不安全的原因】【1. 修改共享數(shù)據(jù) 2. 操作不是原子性 3. 內(nèi)存可見性 4. 代碼順序性】,面試題,JavaEE初階,java-ee,安全,java

【javaEE面試題(四)線程不安全的原因】【1. 修改共享數(shù)據(jù) 2. 操作不是原子性 3. 內(nèi)存可見性 4. 代碼順序性】,面試題,JavaEE初階,java-ee,安全,java
什么是原子性

我們把一段代碼想象成一個(gè)房間,每個(gè)線程就是要進(jìn)入這個(gè)房間的人。如果沒有任何機(jī)制保證,A進(jìn)入房間之后,還沒有出來;B 是不是也可以進(jìn)入房間,打斷 A 在房間里的隱私。這個(gè)就是不具備原子性的。
那我們應(yīng)該如何解決這個(gè)問題呢?是不是只要給房間加一把鎖,A 進(jìn)去就把門鎖上,其他人是不是就進(jìn)不來了。這樣就保證了這段代碼的原子性了。
有時(shí)也把這個(gè)現(xiàn)象叫做同步互斥,表示操作是互相排斥的

一條 java 語(yǔ)句不一定是原子的,也不一定只是一條指令

比如剛才我們看到的 n++,其實(shí)是由三步操作組成的:

  1. 從內(nèi)存把數(shù)據(jù)讀到 CPU
  2. 進(jìn)行數(shù)據(jù)更新
  3. 把數(shù)據(jù)寫回到 CPU

不保證原子性會(huì)給多線程帶來什么問題

如果一個(gè)線程正在對(duì)一個(gè)變量操作,中途其他線程插入進(jìn)來了,如果這個(gè)操作被打斷了,結(jié)果就可能是錯(cuò)誤的。
這點(diǎn)也和線程的搶占式調(diào)度密切相關(guān). 如果線程不是 “搶占” 的, 就算沒有原子性, 也問題不大.

★3. 內(nèi)存可見性

可見性指, 一個(gè)線程對(duì)共享變量值的修改,能夠及時(shí)地被其他線程看到

Java 內(nèi)存模型 (JMM): Java虛擬機(jī)規(guī)范中定義了Java內(nèi)存模型.
目的是屏蔽掉各種硬件和操作系統(tǒng)的內(nèi)存訪問差異,以實(shí)現(xiàn)讓Java程序在各種平臺(tái)下都能達(dá)到一致的并發(fā)效果.

【javaEE面試題(四)線程不安全的原因】【1. 修改共享數(shù)據(jù) 2. 操作不是原子性 3. 內(nèi)存可見性 4. 代碼順序性】,面試題,JavaEE初階,java-ee,安全,java

  • 線程之間的共享變量存在 主內(nèi)存 (Main Memory).
  • 每一個(gè)線程都有自己的 “工作內(nèi)存” (Working Memory) .
  • 當(dāng)線程要讀取一個(gè)共享變量的時(shí)候, 會(huì)先把變量從主內(nèi)存拷貝到工作內(nèi)存, 再?gòu)墓ぷ鲀?nèi)存讀取數(shù)據(jù).
  • 當(dāng)線程要修改一個(gè)共享變量的時(shí)候, 也會(huì)先修改工作內(nèi)存中的副本, 再同步回主內(nèi)存

由于每個(gè)線程有自己的工作內(nèi)存, 這些工作內(nèi)存中的內(nèi)容相當(dāng)于同一個(gè)共享變量的 “副本”. 此時(shí)修改線程1 的工作內(nèi)存中的值, 線程2 的工作內(nèi)存不一定會(huì)及時(shí)變化

  1. 初始情況下, 兩個(gè)線程的工作內(nèi)存內(nèi)容一致【javaEE面試題(四)線程不安全的原因】【1. 修改共享數(shù)據(jù) 2. 操作不是原子性 3. 內(nèi)存可見性 4. 代碼順序性】,面試題,JavaEE初階,java-ee,安全,java
  2. 一旦線程1 修改了 a 的值, 此時(shí)主內(nèi)存不一定能及時(shí)同步. 對(duì)應(yīng)的線程2 的工作內(nèi)存的 a 的值也不一定能及時(shí)同步.【javaEE面試題(四)線程不安全的原因】【1. 修改共享數(shù)據(jù) 2. 操作不是原子性 3. 內(nèi)存可見性 4. 代碼順序性】,面試題,JavaEE初階,java-ee,安全,java
    這個(gè)時(shí)候代碼中就容易出現(xiàn)問題

此時(shí)引入了兩個(gè)問題:

  • 為啥要整這么多內(nèi)存?
  • 為啥要這么麻煩的拷來拷去?
  1. 為啥整這么多內(nèi)存?
    實(shí)際并沒有這么多 “內(nèi)存”. 這只是 Java 規(guī)范中的一個(gè)術(shù)語(yǔ), 是屬于 “抽象” 的叫法.
    所謂的 “主內(nèi)存” 才是真正硬件角度的 “內(nèi)存”. 而所謂的 “工作內(nèi)存”, 則是指 CPU 的寄存器和高速緩存.
  2. 為啥要這么麻煩的拷來拷去?
    因?yàn)?CPU 訪問自身寄存器的速度以及高速緩存的速度, 遠(yuǎn)遠(yuǎn)超過訪問內(nèi)存的速度(快了 3 - 4 個(gè)數(shù)量級(jí), 也就是幾千倍, 上萬(wàn)倍)

比如某個(gè)代碼中要連續(xù) 10 次讀取某個(gè)變量的值, 如果 10 次都從內(nèi)存讀, 速度是很慢的. 但是如果
只是第一次從內(nèi)存讀, 讀到的結(jié)果緩存到 CPU 的某個(gè)寄存器中, 那么后 9 次讀數(shù)據(jù)就不必直接訪問
內(nèi)存了. 效率就大大提高了

那么接下來問題又來了, 既然訪問寄存器速度這么快, 還要內(nèi)存干啥??
答案就是一個(gè)字: 貴

值的一提的是, 快和慢都是相對(duì)的. CPU 訪問寄存器速度遠(yuǎn)遠(yuǎn)快于內(nèi)存, 但是內(nèi)存的訪問速度又遠(yuǎn)遠(yuǎn)快于硬盤.
對(duì)應(yīng)的, CPU 的價(jià)格最貴, 內(nèi)存次之, 硬盤最便宜

★4. 代碼順序性

什么是代碼重排序

一段代碼是這樣的:

  1. 去前臺(tái)取下 U 盤
  2. 去教室寫 10 分鐘作業(yè)
  3. 去前臺(tái)取下快遞

如果是在單線程情況下,JVM、CPU指令集會(huì)對(duì)其進(jìn)行優(yōu)化,比如,按 1->3->2的方式執(zhí)行,也是沒問題,可以少跑一次前臺(tái)。這種叫做指令重排序
編譯器對(duì)于指令重排序的前提是 “保持邏輯不發(fā)生變化”. 這一點(diǎn)在單線程環(huán)境下比較容易判斷, 但是在多線程環(huán)境下就沒那么容易了, 多線程的代碼執(zhí)行復(fù)雜程度更高, 編譯器很難在編譯階段對(duì)代碼的執(zhí)行效果進(jìn)行預(yù)測(cè), 因此激進(jìn)的重排序很容易導(dǎo)致優(yōu)化后的邏輯和之前不等價(jià)

重排序是一個(gè)比較復(fù)雜的話題, 涉及到 CPU 以及編譯器的一些底層工作原理, 此處不做過多討論

4.4 解決之前的線程不安全問題

這里用到的機(jī)制,我們馬上會(huì)給大家解釋文章來源地址http://www.zghlxwxcb.cn/news/detail-539834.html

static class Counter {
    public int count = 0;
    synchronized void increase() {
        count++;
   }
}
public static void main(String[] args) throws InterruptedException {
    final Counter counter = new Counter();
    Thread t1 = new Thread(() -> {
        for (int i = 0; i < 50000; i++) {
            counter.increase();
       }
   });
    Thread t2 = new Thread(() -> {
        for (int i = 0; i < 50000; i++) {
            counter.increase();
       }
   });
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(counter.count);
}

到了這里,關(guān)于【javaEE面試題(四)線程不安全的原因】【1. 修改共享數(shù)據(jù) 2. 操作不是原子性 3. 內(nèi)存可見性 4. 代碼順序性】的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

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

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

相關(guān)文章

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包