發現公司支付寶接入的代碼有點神奇,在網上搜尋了下,找到原始版本。估計有不少人都是抄那份代碼的。
原文在:http://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:
http://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