362
技術社區[雲棲]
從一篇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