首页 > 代码库 > 从一篇Blog看两个并发编程错误

从一篇Blog看两个并发编程错误

发现公司支付宝接入的代码有点神奇,在网上搜索了下,找到原始版本。估计有不少人都是抄那份代码的。

原文在: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