synchronized關鍵字是防止多個線程同時執行一段代碼,那麼就會很影響程序執行效率,而volatile關鍵字在某些狀況下性能要優於synchronized,可是要注意volatile關鍵字是沒法替代synchronized關鍵字的,由於volatile關鍵字沒法保證操做的原子性。一般來講,使用volatile必須具有如下2個條件:html
1)對變量的寫操做不依賴於當前值緩存
2)該變量沒有包含在具備其餘變量的不變式中安全
實際上,這些條件代表,能夠被寫入 volatile 變量的這些有效值獨立於任何程序的狀態,包括變量的當前狀態。併發
事實上,個人理解就是上面的2個條件須要保證操做是原子性操做,才能保證使用volatile關鍵字的程序在併發時可以正確執行。性能
Java語言提供了一種稍弱的同步機制,即volatile變量,用來確保將變量的更新操做通知到其餘線程。當把變量聲明爲volatile類型後,編譯器與運行時都會注意到這個變量是共享的,所以不會將該變量上的操做與其餘內存操做一塊兒重排序。volatile變量不會被緩存在寄存器或者對其餘處理器不可見的地方,所以在讀取volatile類型的變量時總會返回最新寫入的值。優化
在訪問volatile變量時不會執行加鎖操做,所以也就不會使執行線程阻塞,所以volatile變量是一種比sychronized關鍵字更輕量級的同步機制。spa
當對非 volatile 變量進行讀寫的時候,每一個線程先從內存拷貝變量到CPU緩存中。若是計算機有多個CPU,每一個線程可能在不一樣的CPU上被處理,這意味着每一個線程能夠拷貝到不一樣的 CPU cache 中。線程
而聲明變量是 volatile 的,JVM 保證了每次讀變量都從內存中讀,跳過 CPU cache 這一步。htm
1.保證此變量對全部的線程的可見性,這裏的「可見性」,如本文開頭所述,當一個線程修改了這個變量的值,volatile 保證了新值能當即同步到主內存,以及每次使用前當即從主內存刷新。但普通變量作不到這點,普通變量的值在線程間傳遞均須要經過主內存(詳見:Java內存模型)來完成。blog
2.禁止指令重排序優化。有volatile修飾的變量,賦值後多執行了一個「load addl $0x0, (%esp)」操做,這個操做至關於一個內存屏障(指令重排序時不能把後面的指令重排序到內存屏障以前的位置),只有一個CPU訪問內存時,並不須要內存屏障;(什麼是指令重排序:是指CPU採用了容許將多條指令不按程序規定的順序分開發送給各相應電路單元處理)。
volatile 的讀性能消耗與普通變量幾乎相同,可是寫操做稍慢,由於它須要在本地代碼中插入許多內存。
下面從新再闡述下volatile域所遵循的規則: 1.線程所見的值在使用以前總會從主內存中讀出來; 2.線程所寫的值總會在指令完成以前被刷回到主內存中; 如今再看你的第2個結論,並非「中間不會插入其餘線程的操做」,volatile變量在各個線程的工做內存中存在不一致性的問題,可是,在每次使用前都要刷新,因此執行引擎並看不到不一致的狀況,因此能夠認爲不存在不一致性問題。以上兩個規則,保證了可見性。 再看第3個結論,我再補充一些。不安全,指的是 Java 運算並不是原子操做,這是致使 volatile 變量的運算在併發下不安全的緣由。好比「i++」操做,生成四個字節碼指令:getstatic, iconst, iadd, putstatic,volatile 只保證了 getstatic 將 i 值讀取到操做棧頂時 i 值是正確的,可是進行後三個操做前,其餘線程可能進行了 i++操做,操做棧頂的 i 值就是無效的了,這時再 putstatic,就產生了線程不安全。