ajax 示例_通过示例了解挥发
ajax 示例
我們已經花了幾個月的時間來穩定Plumbr中的鎖定檢測功能 。 在此期間,我們遇到了許多棘手的并發問題。 許多問題是獨特的,但是一種特殊類型的問題一直反復出現。
您可能已經猜到了–濫用volatile關鍵字。 我們已經發現并解決了許多問題,其中大量使用volatile使應用程序的任意部分變慢,延長了鎖定保持時間,最終使JVM屈服。 反之亦然-授予過于寬松的訪問策略會引發一些令人討厭的并發問題。
我想每個Java開發人員都會回想起該語言的第一步。 使用手冊和教程的日子和日子。 這些教程都有關鍵字列表,其中volatile是最可怕的關鍵字之一。 隨著時間的流逝,越來越多的代碼不需要使用此關鍵字,我們很多人都忘記了volatile的存在。 直到生產系統開始以無法預測的方式破壞數據或死亡。 調試這種情況迫使我們中的一些人真正理解了這個概念。 但是我敢打賭,這并不是一個令人愉快的課程,因此也許我可以通過一個簡單的例子來闡明一些概念,從而節省一些時間。
揮發作用的例子
該示例模擬了一個銀行辦公室。 銀行辦公室的類型,您可以在該辦公室中從售票機中選擇隊列編號,然后在您前面的隊列得到處理后等待邀請。 為了模擬這樣的辦公室,我們創建了以下示例,該示例由兩個線程組成。
這兩個線程中的第一個被實現為CustomerInLine。 這是一個線程,除了等待NEXT_IN_LINE中的值與客戶的票證匹配外,什么也不做。 票號被硬編碼為#4。 時間到時( NEXT_IN_LINE> = 4),線程宣布等待已結束并結束。 這模擬了有一些客戶在排隊的到達辦公室的客戶。
排隊實現在Queue類中,該類運行一個循環,該循環調用下一個客戶,然后通過為每個客戶Hibernate200ms來模擬與該客戶的工作。 呼叫下一個客戶后,存儲在類變量NEXT_IN_LINE中的值將增加1。
public class Volatility {static int NEXT_IN_LINE = 0;public static void main(String[] args) throws Exception {new CustomerInLine().start();new Queue().start();}static class CustomerInLine extends Thread {@Overridepublic void run() {while (true) {if (NEXT_IN_LINE >= 4) {break;}}System.out.format("Great, finally #%d was called, now it is my turn\n",NEXT_IN_LINE);}}static class Queue extends Thread {@Overridepublic void run() {while (NEXT_IN_LINE < 11) {System.out.format("Calling for the customer #%d\n", NEXT_IN_LINE++);try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}}}} }因此,運行此簡單程序時,您可能希望該程序的輸出類似于以下內容:
Calling for the customer #1 Calling for the customer #2 Calling for the customer #3 Calling for the customer #4 Great, finally #4 was called, now it is my turn Calling for the customer #5 Calling for the customer #6 Calling for the customer #7 Calling for the customer #8 Calling for the customer #9 Calling for the customer #10看來,這個假設是錯誤的。 取而代之的是,您將看到通過10個客戶的列表進行的隊列處理,并且不幸的線程模擬了4號客戶,從不提醒它已經看到邀請。 發生了什么,為什么客戶仍然坐在那里無休止地等待著呢?
分析結果
您在此處面臨的是將JIT優化應用于代碼,該代碼將對NEXT_IN_LINE變量的訪問進行緩存。 兩個線程都有自己的本地副本,并且CustomerInLine線程從不看到Queue實際增加了線程的值。 如果現在您認為這是JVM中的一種可怕的錯誤,那么您就不完全正確了-允許編譯器這樣做以避免每次都重新讀取該值。 因此,您可以提高性能,但要付出代價–如果其他線程更改狀態,則緩存副本的線程將不知道該狀態,并使用過時的值進行操作。
對于volatile正是這種情況。 有了此關鍵字,編譯器將被警告特定狀態是易失的,并且每次執行循環時,代碼都被強制重新讀取該值。 有了這些知識,我們就可以進行簡單的修復-只需將NEXT_IN_LINE的聲明更改為以下內容,您的客戶就不會永遠坐在隊列中:
static volatile int NEXT_IN_LINE = 0;對于那些只了解volatile用例而感到滿意的人,您將很高興。 只是要知道附加的成本–當您開始聲明所有內容都是易失性時,您正在迫使CPU忽略本地緩存并直接進入主內存,從而減慢了代碼的速度并阻塞了內存總線。
引擎蓋下易揮發
對于那些希望詳細了解該問題的人,請和我在一起。 要查看底層發生了什么,讓我們打開調試以查看JIT從字節碼生成的匯編代碼。 通過指定以下JVM選項可以實現此目的:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly在啟用和禁用volatile的情況下啟用這些選項的情況下運行程序,可以為我們提供以下重要見解:
運行不帶volatile關鍵字的代碼,表明我們在指令0x00000001085c1c5a上可以比較兩個值。 當比較失敗時,我們繼續從0x00000001085c1c60到0x00000001085c1c66,后者跳回到0x00000001085c1c60,并產生無限循環。
0x00000001085c1c56: mov 0x70(%r10),%r11d0x00000001085c1c5a: cmp $0x4,%r11d0x00000001085c1c5e: jge 0x00000001085c1c68 ; OopMap{off=64};*if_icmplt; - Volatility$CustomerInLine::run@4 (line 14)0x00000001085c1c60: test %eax,-0x1c6ac66(%rip) # 0x0000000106957000;*if_icmplt; - Volatility$CustomerInLine::run@4 (line 14); {poll}0x00000001085c1c66: jmp 0x00000001085c1c60 ;*getstatic NEXT_IN_LINE; - Volatility$CustomerInLine::run@0 (line 14)0x00000001085c1c68: mov $0xffffff86,%esi使用volatile關鍵字后,我們可以看到在指令0x000000010a5c1c40上我們將值加載到寄存器,在0x000000010a5c1c4a上將其與保護值4進行比較。如果比較失敗,則從0x000000010a5c1c4e跳回0x000000010a5c1c40,再次為新的值加載值檢查。 這確保了我們將看到NEXT_IN_LINE變量的更改后的值。
0x000000010a5c1c36: data32 nopw 0x0(%rax,%rax,1)0x000000010a5c1c40: mov 0x70(%r10),%r8d ; OopMap{r10=Oop off=68};*if_icmplt; - Volatility$CustomerInLine::run@4 (line 14)0x000000010a5c1c44: test %eax,-0x1c1cc4a(%rip) # 0x00000001089a5000; {poll}0x000000010a5c1c4a: cmp $0x4,%r8d0x000000010a5c1c4e: jl 0x000000010a5c1c40 ;*if_icmplt; - Volatility$CustomerInLine::run@4 (line 14)0x000000010a5c1c50: mov $0x15,%esi現在,希望說明能使您擺脫幾個討厭的錯誤。
翻譯自: https://www.javacodegeeks.com/2014/08/understanding-volatile-via-example.html
ajax 示例
總結
以上是生活随笔為你收集整理的ajax 示例_通过示例了解挥发的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OCA第1部分中的Java难题
- 下一篇: 使用Maven进行增量构建