从汇编去分析线程安全
首先要知道什么是線程安全?
當多個線程訪問某個類時,不管運行環境采用何種調度方式或者這些線程將如何交替執行,并且在主調代碼中不需要任何額外的同步或協同,這個類都能表現出正確的行為,那么就稱這個類是線程安全的。
典型線程不安全的列子:
1 import java.util.*; 4 5 class Worker implements Runnable { 6 private UnsafeCount unsafeCount; 7 8 public Worker(UnsafeCount unsafeCount) { 9 this.unsafeCount = unsafeCount; 10 } 11 12 @Override 13 public void run() { 14 // TODO Auto-generated method stub 15 for (int i = 0; i < 1000; i++) 16 unsafeCount.increase(); 17 } 18 19 } 20 21 public class UnsafeCount { 22 private int count = 0; 23 24 public void increase() { 25 count++; 26 } 27 28 public int getCount() { 29 return count; 30 } 31 32 public static void main(String[] args) throws InterruptedException { 33 UnsafeCount uc = new UnsafeCount(); 34 35 //這里用了list簡陋的方式控制線程的結束,更好的實現是用閉鎖CountDownLatch或者柵欄CyclicBarrier 36 List<Thread> list = new ArrayList<Thread>();// 37 38 for (int i = 0; i < 10; i++) { 39 Thread worker = new Thread(new Worker(uc)); 40 worker.start(); 41 list.add(worker); 42 } 43 44 //阻塞直到線程結束 45 for (Thread t : list) { 46 t.join(); 47 } 48 49 System.out.println("total is: " + uc.getCount()); 50 51 } 52 }運行結果(每次結果都不一樣):
total is: 7628
?
我們來仔細分析一下這個結果,開啟10個線程運行,每個線程都對count進行了1000次自增操作,期望的結果應該是1000*10=10000。很明顯運行結果與期望結果不一致。結論是這個類是線程不安全的。為什么會出現這種情況了?
原因是count++這個操作不是原子性,其實這個自增操作是個復合操作:讀-改-寫。 如果我們了解匯編語言的話,對應自增操作的匯編程序可能是:
movl count, %eax #將count的值讀入eax的寄存器中, inc %eax #寄存器eax里的值加1,即改寫count值 movl %eax, %ebx #這里ebx寄存器存存放著count的內存地址,這里是值將改寫的count值寫入到內存中
那么這樣就存在一個問題,假如就存在2個線程A和B操作變量count,初始化時刻count為0. 在線程A未寫入改寫值之前,比如在A線程執行步驟2的時刻, 線程B開始執行,如下所示:
?
線程A讀入count值為0?(步驟1) -》 改寫count值為1(步驟2) -》 將改寫后的count值寫入內存中(步驟3)? 線程A讀入count值為0 (步驟1) -》 改寫count值為1(步驟2) -》 將改寫后的count值寫入內存中(步驟3)
?
因為線程A還沒有更新改寫count的值到內存,這時線程B讀入count的值仍舊是0,導致最后2個線程結束后count的值為1。由此可見做了2次自增的操作,期望結果是2,但實際結果可能是1.這也是線程不安全的情況下,自增的操作的實際結果往往比期望結果小的原因。
?
下篇準備將講什么情況是線程不安全的。
?
?
?
?
?
轉載于:https://www.cnblogs.com/csu_xajy/p/4237489.html
總結
以上是生活随笔為你收集整理的从汇编去分析线程安全的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ubuntu安装环境软件全文档
- 下一篇: 视觉中的经典图像特征小结(一): 颜色直