計算機在執行程序時,指令的執行都是在CPU中。在執行指令的過程當中,會將臨時變量存儲在主存(物理內存)中。 CPU執行指令很快,而從內存中讀取和寫入數據卻相對來講很慢,因此就有了高速緩存。 在程序運行時,先將數據從主存複製一份到高速緩存中,而後CPU執行指令時,就能夠直接從高速緩存中讀取和寫入數據,而後刷新到內存中。編程
這裏就產生了一個問題。 好比i是一個共享變量,初始值爲0緩存
i = i+1;
在單線程中,CPU執行指令時,先從高速緩存中取出數據,而後進行加1操做,再寫入高速緩存中,而後刷新到主存中。 可是多線程中,每個線程都有本身的工做緩存,線程A,B從高速緩存中讀取到數據i的值都爲0,在進行加1操做後,寫入本身的工做緩存中,再刷新到內存中。 進行兩次加法操做,值卻爲1。多線程
這就是緩存不一致問題。併發
解決緩存不一致有兩個解決方法。優化
1.在總線加入LOCK#鎖線程
2.使用緩存一致性協議code
總線加入鎖來阻塞其餘線程訪問時,會致使線程阻塞,使效率低下。排序
緩存一致性協議的核心思想是:當線程對一個共享變量進行寫操做時,會發出通知,讓其餘的線程該變量的緩存行置爲無效,其餘線程操做該變量時,會從新從內存中讀取數據。內存
Java內存模型規定全部的變量都存在主存中,每一個線程都有本身的工做內存(高速緩存),線程的全部操做都只能操做工做內存,不能操做主存,也不能操做其餘線程的工做內存。it
一個操做或多個操做,所有執行不會被其餘操做打斷。 原子性只針對讀取和寫入指令。 好比
i = i + 1;
這個代碼是沒有原子性的,先從高速緩存中取出i的數據,而後進行加1操做,最後寫入高速緩存,再刷新到內存中。
若是要保證這個操做的原子性,就能夠經過synchronized和lock來實現。
多個線程訪問同一個變量時,一個線程修改了變量的值,其餘線程能當即看到修改的值。
Java提供了volatile來保證有效性。
當一個共享變量被volatile修飾時,它保證當線程修改它的值時當即被更新到主存中,其餘線程讀取時,從主存中讀取值。
程序執行順序按照代碼的前後順序執行。
處理器爲了提升程序運行效率,可能會對代碼執行順序進行優化。這裏可能會發生指令重排序。
好比兩個賦值語句
context = init() boolean flag = true;
處理器在執行時,可能先執行flag = true,再執行初始化。
多線程執行時,若是後面有根據flag爲真,對初始化內容的操做。線程1先執行flag=true,還未初始化內容,線程2根據flag爲真執行了對內容的操做,就會致使異常。
while(!flag){ sleep() } dosomething(context);
能夠用synchronized和look保證每一個時刻只有一個線程訪問代碼塊,至關於順序執行代碼。
多個線程對一個volatile修飾的值進行操做時,可能會致使原子性不一致的問題。 由於volatile只保證了可見性和有序性。
對於自增代碼
i++;
因爲該操做不是原子性,因此可能存在一個線程從內存中取出值,以後另外一個線程對該數據取值操做寫入內存。由於可見性是指讀操做,以前線程的值已經取出來了,不會變,以後操做獲得的值就是1,而不是2。
解決方法是該自增代碼加Lock或synchronized