標籤(空格分隔): 進程/線程 操做系統java
Java虛擬機提供的輕量級的同步機制c++
不一樣的線程進入共享內存中讀取數據以後, 在各自的工做空間對數據一通操做, 而後寫入共享內存中, 這個時候由於共享內存的數據改變, 這個時候會通知其餘讀取該共享變量的線程, 通知該數據已經改變.函數
/** * 1. 驗證Volatile的可見性. * 1.1 加入number=0; number沒有添加Volatile關鍵字修飾---沒有可見性. * 1.2 在該線程對其私有虛擬機棧中棧幀中的備份number操做以後, 不會將數據覆蓋到主內存當中. * 2. 加入Volatile以後 * 2.1 在線程對其備份修改完畢以後, 會將數據覆蓋到主內存當中. */ public class VolatileDemo { public static void main(String[] args) { val myData = new MyData(); new Thread(() -> { System.out.println(Thread.currentThread().getName() + "\t come in"); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } myData.addTo60(); System.out.println(Thread.currentThread().getName() + "\t updated number value: " + myData.number); }, "aaa").start(); while (myData.number == 0) { } System.out.println("任務結束, number: " + myData.number); } } class MyData { // volatile int number = 0; int number = 0; public void addTo60() { this.number = 60; } public void addPlusPlus() { this.number++; } }
/** * Volatile 不保證原子性 * * 在多個線程對棧幀中的number修改完畢以後, 在A線程立刻開始寫入主內存的時候被打斷了, 這個時候B線程把本身的計算結果寫入了 * 這個時候, 就會產生計算結果被覆蓋的狀況. 而後永遠都計算不出來正確的值. 這個就是Volatile的原子性問題. */ public class VolatileDemo1 { public static void main(String[] args) throws InterruptedException { MyData myData = new MyData(); for (int i = 0; i < 20; i++) { new Thread(() -> { for (int j = 0; j < 1000; j++) { myData.addPlusPlus(); } }, String.valueOf(i)).start(); } /* main線程 和 後臺GC線程 */ while(Thread.activeCount()>2){ Thread.yield(); } System.out.println(myData.number); System.out.println("剩餘線程數量"+ Thread.activeCount()); } } class MyData { /* 經過 java.util.concurrent.atomic 包去 實現原子性操做. 自旋鎖 * 能夠看到AtomicInteger構造函數發現 最後存儲的方式 private volatile int value; * */ AtomicInteger atomicInteger = new AtomicInteger(1); public int addAtomic(){ return atomicInteger.getAndIncrement(); } }
指令隊列在CPU執行時不是串行的, 當某條指令執行時消耗較多時間時, CPU資源足夠時並不會在此無心義的等待, 而是開啓下一個指令. 開啓下一條指令是有條件的, 即上一條指令和下一條指令不存在相關性. 例以下面這個例子:this
a /= 2; // 指令A a /= 2; // 指令B c++; // 指令C
這裏的指令B是依賴於指令A的執行結果的, 在A處於執行階段時, B會被阻塞, 直到A執行完成. 而指令C與A/B均沒有依賴關係, 因此在A執行或者B執行的過程當中, C會同時被執行, 那麼C有可能在A+B的執行過程當中就執行完畢了, 這樣指令隊列的實際執行順序就是 C->A->B 或者 A->C->B.atom
可能出現指令重排致使問題的代碼操作系統
public void method1() { a = 1; // 語句1 flag = true; // 語句2 } public void method2() { if (flag) { a = a + 5; } }
工做區域和主內存出現的同步延遲現象致使的可見性問題可使用synchronize或volatile解決. 他們均可以使一個線程修改後的變量當即對其餘線程可見.線程
運算由運算器單元(ALU)實現,指令包括算術運算指令、邏輯運算指令和移位指令。
算術運算指令實現加減乘除(+-*/)等基本的算術運算;邏輯運算指令實現與或非(&|~)等基本的邏輯運算;移位指令實現二進制比特位(bit)的左右移(<<>>)運算。code
除了作計算外,CPU還要實現循環。循環是由跳轉指令實現的,跳回去執行就是循環。循環在必定條件下跳出,不然就成死循環了,條件跳轉指令能完成這個功能。條件跳轉指令在必定條件下實現跳轉,它能實現分支功能。跳轉指令也稱爲控制指令。控制由CPU控制器單元實現。隊列
運算和控制指令的操做數從哪裏來的呢?操做數都放在存儲器中。在x86 IA中,運算指令的操做數既能夠是寄存器,也能夠是存儲器;而在其餘RISCIA例如MIPS中,運算指令的操做數只能是寄存器,所以須要先使用加載(load)指令將存儲器中的數據導入到寄存器中,運算完成後,再用存儲(store)指令將寄存器中的運算結果數據導出到存儲器中。這類指令就是數據傳送指令。進程
可見性的實現就是 數據傳送指令中的先使用加載(load)指令將存儲器中的數據導入到寄存器中,運算完成後,再用存儲(store)指令將寄存器中的運算結果數據導出到存儲器中。