閱讀362 返回首頁    go 技術社區[雲棲]


從一篇Blog看兩個並發編程錯誤

發現公司支付寶接入的代碼有點神奇,在網上搜索了下,找到原始版本。估計有不少人都是抄那份代碼的。

原文在:https://blog.csdn.net/simdanfeg/article/details/9011603    Android支付接入(一):支付寶

但是代碼裏有兩個明顯的並發問題,盡管在Android下可能不會有問題。

下麵摘抄一段:

    public class MobileSecurePayer {  
        <strong>Integer lock = 0;  </strong>  
        // 和安全支付服務建立連接  
        private ServiceConnection mAlixPayConnection = new ServiceConnection (){  
            public void onServiceConnected (ComponentName className, IBinder service){  
                //  
                // wake up the binder to continue.  
                // 獲得通信通道  
                <strong>synchronized (lock)</strong>{  
                    mAlixPay = IAlixPay.Stub.asInterface (service);  
                    lock.notify ();  
                }  
            }  


            // 實例一個線程來進行支付  
            new Thread (new Runnable (){  
                public void run (){  
                    try{  
                        // wait for the service bind operation to completely  
                        // finished.  
                        // Note: this is important,otherwise the next mAlixPay.Pay()  
                        // will fail.  
                        // 等待安全支付服務綁定操作結束  
                        // 注意:這裏很重要,否則mAlixPay.Pay()方法會失敗  
                        synchronized (lock){  
 <strong>                           if (mAlixPay == null)  
                                lock.wait (); </strong> 
                        }  

第一個問題:用Integer做lock對象。

在Oracle JDK裏,Integer是有會緩存的,從-128 到127的Integer會緩存到內部的一個IntegerCache,所以兩個Integer可能會是同一個對象。還可以用-XX:AutoBoxCacheMax參數來設置cache的範圍。

另外,即使Integer的內部實現是沒有緩存的,對於像Integer lock = 0; 這樣的代碼,編繹器可能會把它變成一個變量,然後共享這樣一個常量。

所以可能不同的類,會共享了一個synchronized (lock)對象,線程的並發度大大降低,甚至可能會有死鎖。

同理,像Boolean,Float,Long這種類型都是不適合用來做lock對象的。

最好的辦法是直接 Object lock = new Object(); 。


第二個問題:wait方法沒有在while循環裏。

絕大部分情況下,object.wait()方法都應該while循環來判斷條件變量。因為wait()函數可能會因為spurious wakeup而返回。

spurious wakeup請參考另一篇blog:https://blog.csdn.net/hengyunabc/article/details/27969613

另外直接看JDK的文檔,裏麵就說得很清楚:

//As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop: 
     synchronized (obj) {
         while (<condition does not hold>)
             obj.wait();
         ... // Perform action appropriate to condition
     }

最後,支付寶的接入應該去官方的網站找文檔。不過這網站不好找,在支付寶的主頁貌似沒有鏈接過去。。

https://openhome.alipay.com/doc/docIndex.htm

最後更新:2017-04-03 05:38:55

  上一篇:go activty棧管理
  下一篇:go masm中list文件和宏的一些常用編譯調試查看方法