从一篇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

时间: 2024-10-30 12:17:01

从一篇Blog看两个并发编程错误的相关文章

使用 MyBatis 必看两篇文档导读:MyBatis 与 MyBatis-Spring

使用 MyBatis 必看两篇文档导读:MyBatis 与 MyBatis-Spring 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. MyBatis 简介 什么是 MyBatis ? MyB

iOS 10 推送高阶篇(必看)_IOS

推荐阅读: iOS10推送之基础知识(必看篇) 这篇文章开始,我会跟大家好好讲讲,苹果新发布的iOS10的所有通知类. 一.创建本地通知事例详解: 注意啊,小伙伴们,本地通知也必须在appdelegate中注册中心,通知的开关打不打开无所谓的,毕竟是本地通知,但是通知的接收的代理,以及通知点击的代理,苹果给合二为一了.所以大家还是需要在appdelegate中写上这2个方法,还有不要忘记在- (BOOL)application:(UIApplication *)application didFi

Java并发编程:线程池的使用(转)

Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间. 那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务? 在Java中可以通过线程池来达到这样的效果.今天我们就来详细讲解一下Java的线程池,首先我们从最核心的ThreadPool

我为什么喜欢用C#来做并发编程

题记:就语言和运行时层面,C#做并发编程一点都不弱,缺的是生态和社区. 硅谷才女朱赟(我的家门)昨天发了一篇文章<为什么用 Java -- 关于并发编程>,让大家学习了Java中如何进行并发编程的一些基本知识.作为一个将近15年的.NET程序员,我觉得有必要给大家补充介绍一下C#进行并发编程的知识(当然不会太深入讲解).这篇文章无意进行技术比较,毕竟技术只是工具(大同小异,各有千秋),主要还是看用工具的人. 并发(英文Concurrency),其实是一个很泛的概念,字面意思就是"同时

Java并发编程相关面试问题

基础概念 1.什么是原子操作?在Java Concurrency API中有哪些原子类(atomic classes)? 原子操作(atomic operation)意为"不可被中断的一个或一系列操作" .处理器使用基于对缓存加锁或总线加锁的方式来实现多处理器之间的原子操作. 在Java中可以通过锁和循环CAS的方式来实现原子操作. CAS操作--Compare & Set,或是 Compare & Swap,现在几乎所有的CPU指令都支持CAS的原子操作. 原子操作是

并发编程模型

原文链接 作者: Jakob Jenkov 译者: 林威建 [weakielin@gmail.com] 并发系统可以采用多种并发编程模型来实现.并发模型指定了系统中的线程如何通过协作来完成分配给它们的作业.不同的并发模型采用不同的方式拆分作业,同时线程间的协作和交互方式也不相同.这篇并发模型教程将会较深入地介绍目前(2015年,本文撰写时间)比较流行的几种并发模型. 并发模型与分布式系统之间的相似性 本文所描述的并发模型类似于分布式系统中使用的很多体系结构.在并发系统中线程之间可以相互通信.在分

C#并发编程经典实例--并发编程概述

来自 "C#并发编程经典实例" 优秀软件的一个关键特征就是具有并发性.过去的几十年,我们可以进行并发编程,但是难度很大.以前,并发性软件的编写.调试和维护都很难,这导致很多开发人员为图省事放弃了并发编程.新版.NET 中的程序库和语言特征,已经让并发编程变得简单多了.随着Visual Studio 2012 的发布,微软明显降低了并发编程的门槛.以前只有专家才能做并发编程,而今天,每一个开发人员都能够(而且应该)接受并发编程. 1.1 并发编程简介 首先,我来解释几个贯穿本书始终的术语

并发编程(四):也谈谈数据库的锁机制

首先声明,本次文章基本上都是从其他人的文章中或者论坛的回复中整理而来.我把我认为的关键点提取出来供自己学习.所有的引用都附在文后,在这里也就不一一表谢了. 第二个声明,我对于Internel DB并没有研究过,所使用的也是简单的写写SQL,截止到现在最多的一个经验也就是SQL的性能调优,具体点就是通过Postgresql的执行计划,来调整优化SQL语句完成在特定场景下的数据库调优.对于锁,由于数据库支持的锁机制已经能够满足平时的开发需要.因为所从事的行业并不是互联网,没有实时性高并发的应用场景,

C++并发编程那些事(上)

背景介绍 这篇文章主要针对C++11标准发布之后的现代C++的并发编程进行阐述.C++11首次在语言层面承认了多线程的存在,这使得"仅仅使用C++标准库就能编写跨平台的多线程程序"的愿望成为现实. 设计多线程的程序目的主要有两个:充分利用多核CPU的性能(利用多核心的计算能力以及让计算和IO重叠来降低RT并提升吞吐量)和简化程序逻辑(即把单线程状态机的逻辑拆分成多个线程彼此同步,这么做虽然不见得能提升代码性能,但是可以简化代码逻辑).然而有些场合是不合适使用多线程的,最常见的两种场景是