java allocate_Java中volatile關鍵字的最全總結

一、簡介

volatile是Java提供的一種輕量級的同步機制。Java?語言包含兩種內在的同步機制:同步塊(或方法)和?volatile?變量,相比于synchronized(synchronized通常稱為重量級鎖),volatile更輕量級,因為它不會引起線程上下文的切換和調度。但是volatile?變量的同步性較差(有時它更簡單并且開銷更低),而且其使用也更容易出錯。

二、并發編程的3個基本概念

(1)原子性

定義: 即一個操作或者多個操作 要么全部執行并且執行的過程不會被任何因素打斷,要么就都不執行。

原子性是拒絕多線程操作的,不論是多核還是單核,具有原子性的量,同一時刻只能有一個線程來對它進行操作。簡而言之,在整個操作過程中不會被線程調度器中斷的操作,都可認為是原子性。例如 a=1是原子性操作,但是a++和a +=1就不是原子性操作。Java中的原子性操作包括:

a. 基本類型的讀取和賦值操作,且賦值必須是數字賦值給變量,變量之間的相互賦值不是原子性操作。

b.所有引用reference的賦值操作

c.java.concurrent.Atomic.* 包中所有類的一切操作

(2)可見性

定義:指當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其他線程能夠立即看得到修改的值。

在多線程環境下,一個線程對共享變量的操作對其他線程是不可見的。Java提供了volatile來保證可見性,當一個變量被volatile修飾后,表示著線程本地內存無效,當一個線程修改共享變量后他會立即被更新到主內存中,其他線程讀取共享變量時,會直接從主內存中讀取。當然,synchronize和Lock都可以保證可見性。synchronized和Lock能保證同一時刻只有一個線程獲取鎖然后執行同步代碼,并且在釋放鎖之前會將對變量的修改刷新到主存當中。因此可以保證可見性。

(3)有序性

定義:即程序執行的順序按照代碼的先后順序執行。

Java內存模型中的有序性可以總結為:如果在本線程內觀察,所有操作都是有序的;如果在一個線程中觀察另一個線程,所有操作都是無序的。前半句是指“線程內表現為串行語義”,后半句是指“指令重排序”現象和“工作內存主主內存同步延遲”現象。

在Java內存模型中,為了效率是允許編譯器和處理器對指令進行重排序,當然重排序不會影響單線程的運行結果,但是對多線程會有影響。Java提供volatile來保證一定的有序性。最著名的例子就是單例模式里面的DCL(雙重檢查鎖)。另外,可以通過synchronized和Lock來保證有序性,synchronized和Lock保證每個時刻是有一個線程執行同步代碼,相當于是讓線程順序執行同步代碼,自然就保證了有序性。

三、鎖的互斥和可見性

鎖提供了兩種主要特性:互斥(mutual exclusion) 和可見性(visibility)。

(1)互斥即一次只允許一個線程持有某個特定的鎖,一次就只有一個線程能夠使用該共享數據。

(2)可見性要更加復雜一些,它必須確保釋放鎖之前對共享數據做出的更改對于隨后獲得該鎖的另一個線程是可見的。也即當一條線程修改了共享變量的值,新值對于其他線程來說是可以立即得知的。如果沒有同步機制提供的這種可見性保證,線程看到的共享變量可能是修改前的值或不一致的值,這將引發許多嚴重問題。要使?volatile?變量提供理想的線程安全,必須同時滿足下面兩個條件:

a.對變量的寫操作不依賴于當前值。

b.該變量沒有包含在具有其他變量的不變式中。

實際上,這些條件表明,可以被寫入 volatile 變量的這些有效值獨立于任何程序的狀態,包括變量的當前狀態。事實上就是保證操作是原子性操作,才能保證使用volatile關鍵字的程序在并發時能夠正確執行。

四、Java的內存模型JMM以及共享變量的可見性

JMM決定一個線程對共享變量的寫入何時對另一個線程可見,JMM定義了線程和主內存之間的抽象關系:共享變量存儲在主內存(Main Memory)中,每個線程都有一個私有的本地內存(Local Memory),本地內存保存了被該線程使用到的主內存的副本拷貝,線程對變量的所有操作都必須在工作內存中進行,而不能直接讀寫主內存中的變量。

909aeac5a1a5

對于普通的共享變量來講,線程A將其修改為某個值發生在線程A的本地內存中,此時還未同步到主內存中去;而線程B已經緩存了該變量的舊值,所以就導致了共享變量值的不一致。解決這種共享變量在多線程模型中的不可見性問題,較粗暴的方式自然就是加鎖,但是此處使用synchronized或者Lock這些方式太重量級了,比較合理的方式其實就是volatile。

需要注意的是,JMM是個抽象的內存模型,所以所謂的本地內存,主內存都是抽象概念,并不一定就真實的對應cpu緩存和物理內存

五、volatile變量的特性

(1)保證可見性,不保證原子性

a.當寫一個volatile變量時,JMM會把該線程本地內存中的變量強制刷新到主內存中去;

b.這個寫會操作會導致其他線程中的緩存無效。

(2)禁止指令重排

重排序是指編譯器和處理器為了優化程序性能而對指令序列進行排序的一種手段。重排序需要遵守一定規則:

a.重排序操作不會對存在數據依賴關系的操作進行重排序。

比如:a=1;b=a; 這個指令序列,由于第二個操作依賴于第一個操作,所以在編譯時和處理器運

行時這兩個操作不會被重排序。

b.重排序是為了優化性能,但是不管怎么重排序,單線程下程序的執行結果不能被改變

比如:a=1;b=2;c=a+b這三個操作,第一步(a=1)和第二步(b=2)由于不存在數據依賴關系, 所以可能會發

生重排序,但是c=a+b這個操作是不會被重排序的,因為需要保證最終的結果一定是c=a+b=3。

重排序在單線程下一定能保證結果的正確性,但是在多線程環境下,可能發生重排序,影響結果,下例中的1和2由于不存在數據依賴關系,則有可能會被重排序,先執行status=true再執行a=2。而此時線程B會順利到達4處,而線程A中a=2這個操作還未被執行,所以b=a+1的結果也有可能依然等于2。

909aeac5a1a5

.

使用volatile關鍵字修飾共享變量便可以禁止這種重排序。若用volatile修飾共享變量,在編譯時,會在指令序列中插入內存屏障來禁止特定類型的處理器重排序,volatile禁止指令重排序也有一些規則: a.當程序執行到volatile變量的讀操作或者寫操作時,在其前面的操作的更改肯定全部已經進行,且結果已經對后 面的操作可見;在其后面的操作肯定還沒有進行; b.在進行指令優化時,不能將在對volatile變量訪問的語句放在其后面執行,也不能把volatile變量后面的語句放 到其前面執行。 即執行到volatile變量時,其前面的所有語句都執行完,后面所有語句都未執行。且前面語句的結果對volatile變 量及其后面語句可見。

六、volatile不適用的場景

(1)volatile不適合復合操作 例如,in c++不是一個原子性操作,可以由讀取、加、賦值3步組成,所以結果并不能達到30000。

.

909aeac5a1a5

(2)解決方法

1.采用synchronized

909aeac5a1a5

2.采用Lock

909aeac5a1a5

3.采用java并發包中的原子操作類,原子操作類是通過CAS循環的方式來保證其原子性的

909aeac5a1a5

七、volatile原理

volatile可以保證線程可見性且提供了一定的有序性,但是無法保證原子性。在JVM底層volatile是采用“內存屏障”來實現的。觀察加入volatile關鍵字和沒有加入volatile關鍵字時所生成的匯編代碼發現,加入volatile關鍵字時,會多出一個lock前綴指令,lock前綴指令實際上相當于一個內存屏障(也成內存柵欄),內存屏障會提供3個功能:

I. 它確保指令重排序時不會把其后面的指令排到內存屏障之前的位置,也不會把前面的指令排到內

存屏障的后面;即在執行到內存屏障這句指令時,在它前面的操作已經全部完成;

II. 它會強制將對緩存的修改操作立即寫入主存;

III. 如果是寫操作,它會導致其他CPU中對應的緩存行無效。

八、單例模式的雙重鎖為什么要加volatile

909aeac5a1a5

需要volatile關鍵字的原因是,在并發情況下,如果沒有volatile關鍵字,在第5行會出現問題。instance = new TestInstance();可以分解為3行偽代碼

1.memory = allocate()? //分配內存

2. ctorInstanc(memory)? //初始化對象

3. instance = memory? //設置instance指向剛分配的地址

上面的代碼在編譯運行時,可能會出現重排序從1-2-3排序為1-3-2。在多線程的情況下會出現以下問題。線程A在執行第5行代碼時,B線程進來,而此時A執行了1和3,沒有執行2,此時B線程判斷instance不為null,直接返回一個未初始化的對象。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/277448.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/277448.shtml
英文地址,請注明出處:http://en.pswp.cn/news/277448.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

縮放手勢 ScaleGestureDetector 源碼解析,這一篇就夠了

其實在我們日常的編程中,對于縮放手勢的使用并不是很經常,這一手勢主要是用在圖片瀏覽方面,比如下方例子。但是(敲重點),作為 Android 入門的基礎來說,學習 ScaleGestureDetector 的使用&#x…

postgres的數據庫備份和恢復

備份和恢復 一條命令就可以解決很簡單: 這是備份的命令: pg_dump -h 127/0.0.1 -U postgres databasename > databasename.bak 指令解釋: pg_dump 是備份數據庫指令,164.82.233.54是數據庫的ip地址(必須保證數據庫允許外部訪…

java 類的執行順序_Java中類的執行順序

講解在代碼中:package 類執行順序;/*** java類執行順序** 1、如果父類有靜態成員賦值或者靜態初始化塊,執行靜態成員賦值和靜態初始化塊* 2、如果類有靜態成員賦值或者靜態初始化塊,執行靜態成員賦值和靜態初始化塊* 3、將類的成員賦予初值(原…

ZooKeeper相關資料集錦

1、ZooKeeper相關概念總結 https://github.com/Snailclimb/JavaGuide/blob/master/docs/system-design/framework/ZooKeeper.md 2、ZooKeeper在Windows下的安裝和配置 https://blog.csdn.net/morning99/article/details/40426133 3、Curator框架應用 http://ifeve.com/zookeepe…

JQuery.Ajax()的data參數傳遞方式

最近,新學c# mvc,通過ajax post方式傳遞數據到controller。剛開始傳遞參數,controller中總是為null。現記錄一下,可能不全,純粹記個學習日記。 重點在于參數的方式,代碼為例子 1、這里 dataType: "js…

java如何實現封裝_java如何實現封裝

Java中類的封裝是如何實現的封裝是將對象的信息隱藏在對象內部,禁止外部程序直接訪問對象內部的屬性和方法。 java封裝類通過三個步驟實現: (1)修改屬性的可見性,限制訪問。 (2)設置屬性的讀取方法。 (3)在讀取屬性的方法中,添加對…

用了30天整理的一些GO語言學習資料,2019請你加油

因為極其優秀的并發性能,Google的親兒子Go語言站上了風潮之巔。出現在21世紀的GO語言,雖然不能如愿對C取而代之,但是其近C的執行性能和近解析型語言的開發效率以及近乎于完美的編譯速度,已經風靡全球。特別是在云項目中&#xff0…

Kubernetes網絡設計原則

在配置集群網絡插件或者實踐K8S 應用/服務部署請時刻想到這些原則: 1.每個Pod都擁有一個獨立IP地址,Pod內所有容器共享一個網絡命名空間2.集群內所有Pod都在一個直接連通的扁平網絡中,可通過IP直接訪問 所有容器之間無需NAT就可以直接互相訪問…

php token 驗證,PHP如何實現Token驗證

PHP如何實現Token驗證首先將Token進行解析&#xff1b;然后根據解析出來的信息部分驗證是否過期&#xff0c;如果未過期再將解析出的信息部分進行加密&#xff1b;最后將加密出來的數據和解析出來簽名進行比對&#xff0c;如果相同則驗證成功。示例代碼&#xff1a;<?php f…

關于Linux fontconfig 字體庫的坑

01、安裝字體軟件yum -y install fontconfig然后把字體拷過去就行了 cd /usr/share/fonts fc-list 這是查看02、拷貝字體到指定目錄 cp simsun.ttc /usr/share/fonts/然后把字體拷過去就行了 cd /usr/share/fonts 03、驗證字體安裝情況 fc-list //"宋體"中文字體…

滿江紅.互聯網

小小寰球&#xff0c;有多少信息瞬抵。互聯網&#xff0c;幾多濤生&#xff0c;幾多云逸。螞蟻緣槐近大國&#xff0c;菜鳥搭枝成鳳翼。正臺風綠葉下臨安&#xff0c;何足懼?多少事&#xff0c;從來急&#xff1b;天地轉&#xff0c;光陰隙。一百年太久&#xff0c;只爭朝夕。…

Python startswith()函數 與 endswith函數

函數&#xff1a;startswith() 作用&#xff1a;判斷字符串是否以指定字符或子字符串開頭一、函數說明語法&#xff1a;string.startswith(str, beg0,endlen(string)) 或string[beg:end].startswith(str)參數說明&#xff1a;string&#xff1a; 被檢測的字符串str&#xff1a;…

GitLab 在多分支中的一個push

情景&#xff1a;a.本地庫新建的分支&#xff0c;而Git服務器沒有這個分支服務器分支master本地新建分支&#xff1a;rdar-MS&#xff0c;并git checkout rdar-MS上masterrdar-testrdar-MS更改rdar-MS分支上的文件&#xff0c;git add .git commit -m " "后&#xf…

php post 獲取xml,php 獲取post的xml數據并解析示例

這篇文章主要為大家詳細介紹了php 獲取post的xml數據并解析示例&#xff0c;具有一定的參考價值&#xff0c;可以用來參考一下。對php獲取post過來的xml數據并解析感興趣的小伙伴&#xff0c;下面一起跟隨512筆記的小編兩巴掌來看看吧&#xff01;如何獲取請求的xml數據,對方通…

值得一用的Windows軟件

該清單僅本人使用后所作推薦&#xff0c;可能會比較主觀&#xff0c;所以僅供參考哈。可能某些軟件鏈接會失效&#xff0c;可以自行百度搜索下載即可。 殺軟 火絨安全&#xff1a;國內殺毒軟件的一股清流&#xff0c;界面簡潔&#xff0c;無推廣。現在已經開啟了 5.0 公測&…

《JavaScript模式》讀書筆記一:基本技巧

《JavaScript模式》的讀書筆記&#xff0c;個人向&#xff01;更新進度隨我的閱讀進度 基本技巧 盡量少用全局變量 防止變量污染注意JS變量提升問題盡量使用單一var模式&#xff0c;只使用一個var在函數頂部進行變量聲明function fun () {var a 1,b2,sum ab,函數體//} for循環…

Python字符串處理全攻略(四):常用內置方法輕松掌握

文章目錄 引言Python字符串常用內置方法切片功能介紹語法示例注意事項 str.isalpha()功能介紹語法示例注意事項 str.isdigit()功能介紹語法示例注意事項總結 str.isalnum()功能介紹語法示例注意事項總結 str.isupper()功能介紹語法示例注意事項 islower()功能介紹語法示例注意事…

php空間限制磁盤限額,ORA-01536:超出表空間XXXX的空間限額

問題描述&#xff1a;在FMIS2600用戶下進行某個DDL或DML操作時&#xff0c;提示&#xff1a;ORA-01536&#xff1a;超出表空間FMIS2600 的空間限額 或者 ORA-01950: 對表空間/*******************ORA-01536&#xff1a;超出表空間XXXX的空間限額*******************//*********…

01爬蟲基本原理及Requests庫下載

一、爬蟲基本原理 1.什么是爬蟲 ? 爬蟲就是爬取數據 2.什么是互聯網&#xff1f; ? 就是由一堆網絡設備&#xff0c;把一臺臺的電腦互聯在一起 3.互聯網建立的目的 ? 數據的傳遞和數據共享 4.什么是數據&#xff1f; ? 例如&#xff1a; ? 電商平臺的商業信息&#xff08;…

php 怎么實現收藏功能,php收藏功能如何實現

php收藏功能如何實現php收藏功能的實現方法&#xff1a;首先創建好數據庫表 &#xff1b;然后創建前臺代碼&#xff0c;實現登錄界面&#xff1b;接著通過html實現收藏樣式&#xff1b;最后使用php進行后臺處理即可。推薦&#xff1a;《PHP視頻教程》這是數據庫表話不多說上代碼…