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

简介: 发现公司支付宝接入的代码有点神奇,在网上搜索了下,找到原始版本。估计有不少人都是抄那份代码的。原文在:http://blog.csdn.net/simdanfeg/article/details/9011603    Android支付接入(一):支付宝但是代码里有两个明显的并发问题,尽管在Android下可能不会有问题。

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

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

相关文章
|
7月前
|
C#
C#学习相关系列之多线程---ConfigureAwait的用法
C#学习相关系列之多线程---ConfigureAwait的用法
128 0
|
7月前
|
C#
C#学习相关系列之多线程---TaskCompletionSource用法(八)
C#学习相关系列之多线程---TaskCompletionSource用法(八)
192 0
|
Go 调度 数据安全/隐私保护
后端实践--go并发编程 青训营
后端实践--go并发编程 青训营
|
安全 Go
大白话讲讲 Go 语言的 sync.Map(二)
上一篇文章《大白话讲讲 Go 语言的 sync.Map(一)》讲到 entry 数据结构,原因是 Go 语言标准库的 map 不是线程安全的,通过加一层抽象回避这个问题……
115 1
|
存储 程序员 Go
大白话讲讲 Go 语言的 sync.Map(一)
在讲 sync.Map 之前,我们先说说什么是 map(映射)。我们每个人都有身份证号码,如果我需要从身份证号码查到对应的姓名,用 map 存储是非常合适的……
126 1
上篇:Go并发编程
上篇:Go并发编程
85 0
|
Java 数据处理 调度
Java Review - 并发编程_锁的分类
Java Review - 并发编程_锁的分类
90 0
|
存储 缓存 算法
Java Review - 并发编程_伪共享
Java Review - 并发编程_伪共享
95 0
|
安全 测试技术 Go
Go 语言入门很简单 -- 16. Go 并发互斥锁 #私藏项目实操分享#
Go 语言入门很简单 -- 16. Go 并发互斥锁 #私藏项目实操分享#
156 0
Go 语言入门很简单 -- 16. Go 并发互斥锁 #私藏项目实操分享#
|
缓存 并行计算 安全
Java Review(三十七、多线程)
Java Review(三十七、多线程)
193 0
Java Review(三十七、多线程)