摘要:最近在看多線程相關,看到這篇來自大神關于關鍵字的講解感覺非常詳細易懂,特此轉載一下。如果對增加聲明則所有線程對的寫都會立即刷新到主存中,而且所有對的讀也都直接從主存中去讀。
最近在看java多線程相關,看到這篇來自大神Jakob Jenkov關于Volatile關鍵字的講解感覺非常詳細易懂,特此轉載一下。
原文鏈接:http://tutorials.jenkov.com/j...
在多線程應用中,對于每個非Volatile變量,每個cpu會從內存中拷貝一份副本到cpu的高速緩存中。
假設一種場景,兩個或兩個以上的線程去訪問共享對象SharedObject
public class SharedObject { public int counter = 0; }
線程1去寫counter值,線程1和線程2會不定期的讀counter的值,則有可能存在兩個cpu中緩存的counter值不一樣,cpu1寫入到緩存中的counter值沒有刷新到主存,如下圖的情況:
這就叫可見性問題。
在java中,Volatile關鍵字可以保證變量的可見性。如果對counter增加Volatile聲明:
public class SharedObject { public volatile int counter = 0; }
則所有線程對counter的寫都會立即刷新到主存中,而且所有對counter的讀也都直接從主存中去讀。
Volatile關鍵字的可見性保證不僅限于被Volatile聲明的變量本身還有以下兩種變量即使沒有用volatile聲明,也可以得到相應的可見性保證
如果線程A去寫一個volatile變量,隨后線程B去讀這個volatile變量,則所有在線程A寫這個volatile變量之前對線程A可見的變量,在線程B讀這個volatile變量之后也對線程B可見。
如果線程A去讀一個volatile變量,則所有對線程A可見的變量,在線程A讀取這個volatile變量時,會從主存中重新讀一次
晦澀難懂,舉個栗子:
解釋第一條:
public class MyClass { private int years; private int months private volatile int days; public void update(int years, int months, int days){ this.years = years; this.months = months; this.days = days; } }
當days變量被寫入時,years和months因為對當前線程可見,也會被寫入到主存中。
解釋第二條:
ublic class MyClass { private int years; private int months private volatile int days; public int totalDays() { int total = this.days; total += months * 30; total += years * 365; return total; } public void update(int years, int months, int days){ this.years = years; this.months = months; this.days = days; } }
totalDays()方法執行時,第一步讀取days的值,因為days時volatile變量,所以所有對當前線程可見的變量都會從主存中重新讀一次。
但是這樣對帶來一個由重排序而產生的新問題為了提高性能,CPU和JVM都會指令在不影響語義的前提下進行重排序操作,舉個栗子:
上面的update方法可能會被重排序成:(為了方便理解,將參數名加了個new后綴)
public class MyClass { private int years; private int months private volatile int days; public void update(int years, int monthsNew, int daysNew){ this.days = days; this.months = monthsNew; this.years = daysNew; } }
這種情況下雖然在days被寫入的時候,years和months也會被刷新到主存中,但是并不是monthsNew和daysNew的值,這就意味著,這種重排序改變了語義。
重排序問題的解決方案:volatile的 Happens-Before 原則為解決這個問題,java賦予了volatile變量一些 Happens-Before 原則的保證。
原則如下:
對其它變量的讀和寫操作不能被重排序到對一個volatile變量的寫操作之后。
對其他變量的讀和寫操作不能被重排序到對一個volatile變量的讀操作之前。
注意:兩條原則反過來是不保證的,比如對其它變量的讀和寫操作有可能會被重排序到對一個volatile變量的寫操作之前。
未完待續~~~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69563.html
摘要:現在的處理器,一般是會有多個的,每個線程可能運行在不同的,那么線程修改完成的值,是首先保存在中去。考慮這樣一種情況現在有兩個線程,線程和線程,他們不時會去讀取這個共享變量。 什么是volatile 關鍵字volatile 提供了Java 虛擬機中最輕量級的同步機制。在meidium 中有篇文章說:Volatile?specifier?is used to indicate that a...
摘要:前半句是指線程內表現為串行的語義,后半句是指指令重排序現象和工作內存和主內存同步延遲現象。關于內存模型的講解請參考死磕同步系列之。目前國內市面上的關于內存屏障的講解基本不會超過這三篇文章,包括相關書籍中的介紹。問題 (1)volatile是如何保證可見性的? (2)volatile是如何禁止重排序的? (3)volatile的實現原理? (4)volatile的缺陷? 簡介 volatile...
摘要:前半句是指線程內表現為串行的語義,后半句是指指令重排序現象和工作內存和主內存同步延遲現象。關于內存模型的講解請參考死磕同步系列之。目前國內市面上的關于內存屏障的講解基本不會超過這三篇文章,包括相關書籍中的介紹。問題 (1)volatile是如何保證可見性的? (2)volatile是如何禁止重排序的? (3)volatile的實現原理? (4)volatile的缺陷? 簡介 volatile...
摘要:前半句是指線程內表現為串行的語義,后半句是指指令重排序現象和工作內存和主內存同步延遲現象。關于內存模型的講解請參考死磕同步系列之。目前國內市面上的關于內存屏障的講解基本不會超過這三篇文章,包括相關書籍中的介紹。問題 (1)volatile是如何保證可見性的? (2)volatile是如何禁止重排序的? (3)volatile的實現原理? (4)volatile的缺陷? 簡介 volatile...
摘要:掌握的內存模型,你就是解決并發問題最靚的仔編譯優化說的具體一些,這些方法包括和關鍵字,以及內存模型中的規則。掌握的內存模型,你就是解決并發問題最靚的仔共享變量藍色的虛線箭頭代表禁用了緩存,黑色的實線箭頭代表直接從主內存中讀寫數據。 摘要:如果編寫的并發程序出現問題時,很難通過調試來解決相應的問題,此時,需要一行行的檢查代碼...
閱讀 3712·2023-04-25 17:45
閱讀 3426·2021-09-04 16:40
閱讀 999·2019-08-30 13:54
閱讀 2126·2019-08-29 12:59
閱讀 1396·2019-08-26 12:11
閱讀 3273·2019-08-23 15:17
閱讀 1516·2019-08-23 12:07
閱讀 3878·2019-08-22 18:00