国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當前位置: 首頁 > news >正文

給網(wǎng)站劃分欄目怎么做百度關(guān)鍵詞排名

給網(wǎng)站劃分欄目,怎么做百度關(guān)鍵詞排名,wordpress郵件通知代碼,自己設計服裝的app免費volatile 關(guān)鍵字 volatile 能保證內(nèi)存可見性 volatile 修飾的變量, 能夠保證 “內(nèi)存可見性” 示例代碼: 運行結(jié)果: 當輸入1(1是非O)的時候,但是t1這個線程并沿有結(jié)束循環(huán), 同時可以看到,t2這個線程已經(jīng)執(zhí)行完了,而t1線程還在繼續(xù)循環(huán). 這個情況,就叫做內(nèi)存可見性問題 ~~ 這…

請?zhí)砑訄D片描述

volatile 關(guān)鍵字

volatile 能保證內(nèi)存可見性
volatile 修飾的變量, 能夠保證 “內(nèi)存可見性”

示例代碼:

image-20230927234356426

運行結(jié)果:
image-20230927234904570

當輸入1(1是非O)的時候,但是t1這個線程并沿有結(jié)束循環(huán),
同時可以看到,t2這個線程已經(jīng)執(zhí)行完了,而t1線程還在繼續(xù)循環(huán).

這個情況,就叫做內(nèi)存可見性問題 ~~ 這也是一個線程不安全問題(一個線程讀,一個線程改)

while (myCounter.flag == 0) { // 循環(huán)體空著,什么也不做 }

這里使用匯編來理解,大概就是兩步操作:

  1. load, 把內(nèi)存中 flag 的值,讀取到寄存器里.
  2. cmp, 把寄存器的值,和0進行比較,根據(jù)比較結(jié)果,決定下一步往哪個地方執(zhí)行(條件跳轉(zhuǎn)指令).

上述是個循環(huán),這個循環(huán)執(zhí)行速度極快,一秒鐘執(zhí)行百萬次以上…
循環(huán)執(zhí)行這么多次,在線程 t2 真正修改之前, load得到的結(jié)果都是一樣的;
另一方面, load操作和cmp操作相比,速度慢非常非常多!!!

注:
CPU針對寄存器的操作,要比內(nèi)存操作快很多,快3-4數(shù)量級;
計算機對于內(nèi)存的操作,比硬盤快3-4個數(shù)量級.

由于 load 執(zhí)行速度太慢(相比于cmp來說),再加上反復 load 到的結(jié)果都一樣, JVM 就做出了一個非常大膽的決定 ~~ 判定好像沒人改 flag 值,不再真正的重復 load 了,干脆就只讀取一次就好了 => 編譯器優(yōu)化的一種方式.
實際上是有人在修改的,但是 JVM/編譯器 對于這種多線程的情況,判定可能存在誤差.
此時,就需要我們手動干預了,可以給 flag 這個變量加上 volatile 關(guān)鍵字,意思就是告訴編譯器,這個變量是"易變"的,要每次都重新讀取這個變量的內(nèi)存內(nèi)容,不能再進行激進的優(yōu)化了.
博主感慨: 快和準之間往往不可兼得

內(nèi)存可見性問題

一個線程針對一個變量進行讀取操作,同時另一個線程針對這個變量進行修改,此時讀到的值,不一定是修改之后的值;這個讀線程沒有感知到變量的變化;
歸根結(jié)底是 編譯器/JVM 在多線程環(huán)境下優(yōu)化時產(chǎn)生了誤判了.
備注:
(1)上述說的內(nèi)存可見性編譯器優(yōu)化的問題,也不是始終會出現(xiàn)的(編譯器可能存在誤判,也不是100%就誤判!);
(2)編譯器的優(yōu)化,很多時候是“玄學問題”,應用程序這個角度是無法感知的.編譯器的優(yōu)化,很多時候是“玄學問題”,應用程序這個角度是無法感知的.

代碼改正:

class MyCounter {volatile public int flag = 0;
}

運行結(jié)果:

image-20230928004924362

注意事項:
(1) volatile 只能修飾變量;
(2) volatile 不能修飾方法的局部變量,局部變量只能在你當前線程里面用,不能多線程之間同時讀取/修改(天然就規(guī)避了線程安全問題);

(1)局部變量只能在當前方法里使用的,出了方法變量就沒了,方法內(nèi)部的變量在"棧”這樣的內(nèi)存空間上;
(2)每個線程都有自己的??臻g,即使是同一個方法,在多個線程中被調(diào)用,這里的局部變量也會處在不同的??臻g中,本質(zhì)上是不同變量,也就涉及不到修改/讀取同一個變量的操作;
(3)棧記錄了方法之間的調(diào)用關(guān)系;
個人理解: 局部變量只對當前線程可見,其他線程看不了.

(3) 如果一個變量在兩個線程中,一個讀,一個寫,就需要考慮volatile 了;
(4) volatile 不保證原子性,原子性是靠 synchronized 來保證的. synchronized 和 volatile 都能保證線程安全 => 不能使用 volatile 處理兩個線程并發(fā)++這樣的問題;
(5) 如果涉及到某個代碼,既需要考慮原子性,有需要考慮內(nèi)存可見性,就把 synchronized 和 volatile 都用上就行了.


從 JMM 的角度重新表述內(nèi)存可見性問題

內(nèi)存可見性問題,其他的一些資料,談到了JMM(Java Memory Mode ~~ Java內(nèi)存模型)
從 JMM 的角度重新表述內(nèi)存可見性問題(Java的官方文檔的大概表述):
Java 程序里,主內(nèi)存,每個線程還有自己的工作內(nèi)存(線程 t1 的和線程 t2 的工作內(nèi)存不是同一個東西);
線程 t1 進行讀取的時候,只是讀取了工作內(nèi)存的值;
線程 t2進行修改的時候,先修改的工作內(nèi)存的值,然后再把工作內(nèi)存的內(nèi)容同步到主內(nèi)存中,但是由于編譯器優(yōu)化,導致線程 t1沒有重新的從主內(nèi)存同步數(shù)據(jù)到工作內(nèi)存,讀到的結(jié)果就是“修改之前"的結(jié)果.

如果把"主內(nèi)存”代替成"內(nèi)存",把“工作內(nèi)存"代替成"CPU寄存器",就容易理解.
注: 之所以上面這段話這么晦澀,是翻譯不行,翻譯官得背鍋 ~~ 翻譯的結(jié)果讓人誤會了!!!
主內(nèi)存: main memory => 主存,也就是平時所說的內(nèi)存
工作內(nèi)存: work memory =>工作存儲區(qū),并非是所說的內(nèi)存,而是CPU上存儲數(shù)據(jù)的單元(寄存器)

為什么Java這里,不直接叫做“CPU寄存器",而是專門搞了"工作內(nèi)存”說法呢?

這里的工作內(nèi)存,不一定只是CPU的寄存器,還可能包括CPU的緩存cache.

image-20230928013620585

當CPU要讀取一個內(nèi)存數(shù)據(jù)的時候,可能是直接讀內(nèi)存也可能是讀cache還能是讀寄存器…
引入cache之后,硬件結(jié)構(gòu)就更復雜了,工作內(nèi)存(工作存儲區(qū)): CPU寄存器 + CPU的cache;
一方面是為了表述簡單,另一方面也是為了避免涉及到硬件的細節(jié)和差異,Java里就使用"工作內(nèi)存"這個詞來統(tǒng)稱(泛指)了;畢竟,現(xiàn)實中有的 CPU 可能沒有 cache, 有的 CPU 有;有的 CPU 可能有一個cache,還可能有多個;現(xiàn)代的 CPU 普遍是3級cache, L1, L2, L3,總之,情況多樣.
注: 學校的"計算機系統(tǒng)結(jié)構(gòu)”會講解CPU內(nèi)部的結(jié)構(gòu),尤其是寄存器, cache,指令等等,上這門課的時候要好好聽講.

wait 和 notify

線程最大的問題,是搶占式執(zhí)行,隨機調(diào)度~~
程序猿寫代碼,不喜歡隨機,喜歡確定的東西,于是發(fā)明了一些辦法,來控制線程之間的執(zhí)行順序,雖然線程在內(nèi)核里的調(diào)度是隨機的,但是可以通過一些 API 讓線程主動塞,主動放棄CPU(給別的線程讓路).
比如,t1,t2 兩個線程,希望t1先干活,干的差不多了,再讓t2來干.就可以讓t2先wait(阻塞,主動放棄CPU)等t1干的差不多了,再通過notify 通知t2,把t2喚醒,讓t2接著干.

那么上述場景,使用 join 或者 sleep行不行呢?

使用join,則必須要t1徹底執(zhí)行完,t2才能運行.如果是希望t1先干50%的活,就讓t2開始行動,join無能為力.
使用sleep,指定一個休眠時間的,但是t1執(zhí)行的這些活,到底花了多少時間,不好估計.
使用wait和notify可以更好的解決上述的問題.

注: wait, notify, notifyAll 這幾個類,都是Object類(Java里所有類的祖宗)的方法.Java里隨便new個對象,都可以有這三個方法!!

wait

wait 進行阻塞.某個線程調(diào)用wait方法,就會進入阻塞(無論是通過哪個對象 wait的),此時就處在WAITING狀態(tài).
wait 不加任何參數(shù),就是一個"死等"一直等待,直到有其它線程喚醒它.
示例代碼:

public class ThreadDemo16 {public static void main(String[] args) throws InterruptedException {Object object = new Object();object.wait();}
}

throws InterruptedException : 這個異常,很多帶有阻塞功能的方法都帶.這些方法都是可以 interrupt 方法通過這個異常給喚醒的.

運行結(jié)果:

image-20230927204141554

IllegalMonitorStateException
~~ 非法的鎖狀態(tài)異常
~~ 鎖的狀態(tài),無非就是被加鎖的狀態(tài)和和被解鎖的狀態(tài).

為什么有這個異常,要先理解 wait 的操作是干什么了.
1.先釋放鎖
2.進行阻塞等待
3.收到通知之后,重新嘗試獲取鎖,并且在獲取鎖后,繼續(xù)往下執(zhí)行.

這里鎖狀態(tài)異常,就是沒加鎖呢,就想著釋放鎖.就好比單身著呢,就想著分手.

public static void main(String[] args) throws InterruptedException {Object object = new Object();synchronized (object) {System.out.println("wait 之前");object.wait();System.out.println("wait 之后");}}

image-20230927205029017

雖然這里wait是阻塞了,阻塞在 synchronized 代碼塊里,實際上,這里的阻塞是釋放了鎖的,此時其他線程是可以獲取到object這個對象的鎖的,此時這里的阻塞,就處在WAITING狀態(tài).

image-20230928094946950

t1.start();
t2.start();

如果代碼這里寫作 t1.start 和 t2.start 由于線程調(diào)度的不確定性,此時不能保證一定是先執(zhí)行 wait ,后執(zhí)行notify. 如果調(diào)用notify,此時沒有線程wait,此處的wait是無法被喚醒的!!!(這種通知就是無效通知).
因此此處的代碼還是要盡量保證先執(zhí)行wait后執(zhí)行notify才是有意義的.

改正的代碼:

   public static void main(String[] args) throws InterruptedException {Object object = new Object();Thread t1 = new Thread(() -> {// 這個線程負責進行等待System.out.println("t1: wait 之前");try {synchronized (object) {object.wait();}} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("t2: wait 之后");});Thread t2 = new Thread(() -> {System.out.println("t2: notify 之前");synchronized (object) {// notify 務必要獲取到鎖,才能進行通知object.notify();}System.out.println("t2: notify 之后");});t1.start();// 此處寫的 sleep 500 是大概率會讓 t1 先執(zhí)行 wait 的// 極端情況下,電腦特別卡的時候, 可能線程的調(diào)度時間就超過了 500 ms// 還是可能 t2 先執(zhí)行 notifyThread.sleep(500);t2.start();}

運行結(jié)果:

image-20230927214510375

此處,先執(zhí)行了wait,很明顯wait操作阻塞了,沒有看到wait之后的打印;
接下來執(zhí)行到了t2, t2進行了notify的時候,才會把t1的wait喚醒.t1才能繼續(xù)執(zhí)行.
只要t2不進行notify,此時t1就會始終wait下去(死等).

wait無參數(shù)版本,就是死等的.
wait帶參數(shù)版本,指定了等待的最大時間.

wait的帶有等待時間的版本,看起來就和sleep有點像.其實還是有本質(zhì)差別的:
雖然都是能指定等待時間,也都能被提前喚醒(wait是使用notify 喚醒, sleep使用interrupt喚醒)但是這里表示的含義截然不同.
notify喚醒wait,這是不會有任何異常的.(正常的業(yè)務邏輯),interrupt喚醒sleep 則是出異常了(表示一個出問題了的邏輯).

如果當前有多個線程在等待object對象,此時有一個線程 object.notify(),此時是隨機喚醒一個等待的線程.(不知道具體是哪個),但是,可以用多組不同的對象來控制線程的執(zhí)行順序.
比如,有三個線程,希望先執(zhí)行線程1,再執(zhí)行線程2,再執(zhí)行線程3,
創(chuàng)建obj1,供線程1,2使用創(chuàng)建obj2,供線程2,3使用線程3, obj2.wait
線程2.obj1.wait(),喚醒之后執(zhí)行obj2.notify()
線程1執(zhí)行自己的任務,執(zhí)行完了之后,obj1.notify即可.

notifyAll和notify非常相似.
多個線程 wait 的時候, notify隨機喚醒一個, notifyAll 所有線程都喚醒,這些線程再一起競爭鎖…

http://www.aloenet.com.cn/news/31519.html

相關(guān)文章:

  • 做網(wǎng)站用什么框架好怎么引流怎么推廣自己的產(chǎn)品
  • 公司網(wǎng)站郵箱費用熱搜詞排行榜關(guān)鍵詞
  • 鄭州建站價格手機百度app下載安裝
  • Wordpress 視頻采集插件電商seo優(yōu)化是什么意思
  • 漳州微網(wǎng)站建設公司推薦百度廣告聯(lián)系方式
  • 網(wǎng)站建設多長時間能學會做網(wǎng)站平臺需要多少錢
  • 怎么查詢網(wǎng)站備案培訓學校怎么招生
  • 如何做直播做菜視頻網(wǎng)站關(guān)鍵詞優(yōu)化排名
  • 西鄉(xiāng)網(wǎng)站開發(fā)長沙seo關(guān)鍵詞排名
  • 工藝品做網(wǎng)站網(wǎng)絡推廣公司
  • wordpress指定用戶隱藏分類廣州seo站內(nèi)優(yōu)化
  • 鄭州做網(wǎng)站推廣價格徐州seo排名收費
  • 廣州網(wǎng)站設計出名 樂云踐新正規(guī)推廣平臺有哪些
  • 外貿(mào)手機網(wǎng)站seo標題優(yōu)化的心得總結(jié)
  • 網(wǎng)站設計實例搜狗快速收錄方法
  • 沒有照片怎么做網(wǎng)站教育培訓網(wǎng)站大全
  • 廣西住房建設廳網(wǎng)站搜索引擎營銷案例分析題
  • 西安住房和城鄉(xiāng)建設局網(wǎng)站如何讓自己的網(wǎng)站快速被百度收錄
  • 政府網(wǎng)站內(nèi)容建設方案怎么免費注冊域名
  • 保定網(wǎng)站定制公司seo少女
  • 建站廣告?zhèn)€人網(wǎng)站首頁設計
  • 邢臺企業(yè)做網(wǎng)站費用哪里可以建網(wǎng)站
  • 瑞麗網(wǎng)站建設深圳整站seo
  • 湖南做網(wǎng)站 真好磐石網(wǎng)絡東莞公司網(wǎng)上推廣
  • 網(wǎng)站建設銷售發(fā)展前景百度指數(shù)關(guān)鍵詞搜索趨勢
  • 英文seo公司seo文章
  • 網(wǎng)站域名使用期怎么去推廣自己的店鋪
  • 做ar的網(wǎng)站搜資源的搜索引擎
  • 婦聯(lián)網(wǎng)站建設方案搜索歷史記錄
  • 熟人做網(wǎng)站怎么收錢湖南seo服務電話