首页 文章 精选 留言 我的

精选列表

搜索[Provider桥接],共6067篇文章
优秀的个人博客,低调大师

Android应用程序组件Content Provider的共享数据更新通知机制分析(2)

Step 1. ContentResolver.registerContentObserver 这个函数定义在frameworks/base/core/java/android/content/ContentResolver.java文件中: publicabstractclassContentResolver{ ...... publicfinalvoidregisterContentObserver(Uriuri,booleannotifyForDescendents, ContentObserverobserver) { try{ getContentService().registerContentObserver(uri,notifyForDescendents, observer.getContentObserver()); }catch(RemoteExceptione){ } } ...... } 当参数notifyForDescendents为true时,表示要监控所有以uri为前缀的URI对应的数据变化。这个函数做了三件事情,一是调用getContentService函数来获得前面已经启动起来了的ContentService远程接口,二是调用从参数传进来的ContentObserver对象observer的getContentObserver函数来获得一个Binder对象,三是通过调用这个ContentService远程接口的registerContentObserver函数来把这个Binder对象注册到ContentService中去。 Step 2.ContentResolver.getContentService 这个函数定义在frameworks/base/core/java/android/content/ContentResolver.java文件中: publicabstractclassContentResolver{ ...... publicstaticIContentServicegetContentService(){ if(sContentService!=null){ returnsContentService; } IBinderb=ServiceManager.getService(CONTENT_SERVICE_NAME); ...... sContentService=IContentService.Stub.asInterface(b); ...... returnsContentService; } privatestaticIContentServicesContentService; ...... } 在ContentResolver类中,有一个静态成员变量sContentService,开始时它的值为null。当ContentResolver类的getContentService函数第一次被调用时,它便会通过ServiceManager类的getService函数来获得前面已经启动起来了的ContentService服务的远程接口,然后把它保存在sContentService变量中。这样,当下次ContentResolver类的getContentService函数再次被调用时,就可以直接把这个ContentService远程接口返回给调用者了。 Step 3.ContentObserver.getContentObserver 这个函数定义在frameworks/base/core/java/android/database/ContentObserver.java文件中: publicabstractclassContentObserver{ ...... privateTransportmTransport; ...... privatestaticfinalclassTransportextendsIContentObserver.Stub{ ContentObservermContentObserver; publicTransport(ContentObservercontentObserver){ mContentObserver=contentObserver; } ...... } ...... publicIContentObservergetContentObserver(){ synchronized(lock){ if(mTransport==null){ mTransport=newTransport(this); } returnmTransport; } } ...... } ContentObserver类的getContentObserver函数返回的是一个成员变量mTransport,它的类型为ContentObserver的内部类Transport。从Transport类的定义我们可以知道,它有一个成员变量mContentObserver,用来保存与对应的ContentObserver对象。同时我们还可以看出,ContentObserver类的成员变量mTransport是一个Binder对象,它是要传递给ContentService服务的,以便当ContentObserver所监控的数据发生变化时,ContentService服务可以通过这个Binder对象通知相应的ContentObserver它监控的数据发生变化了。 Step 4. ContentService.registerContentObserver 这个函数定义在frameworks/base/core/java/android/content/ContentService.java文件中: publicfinalclassContentServiceextendsIContentService.Stub{ ...... privatefinalObserverNodemRootNode=newObserverNode(""); ...... publicvoidregisterContentObserver(Uriuri,booleannotifyForDescendents, IContentObserverobserver){ ...... synchronized(mRootNode){ mRootNode.addObserverLocked(uri,observer,notifyForDescendents,mRootNode); ...... } } ...... } 它调用了ContentService类的成员变量mRootNode的addObserverLocked函数来注册这个ContentObserver对象observer。成员变量mRootNode的类型为ContentService在内部定义的一个类ObserverNode。 Step 5.ObserverNode.addObserverLocked 这个函数定义在frameworks/base/core/java/android/content/ContentService.java文件中: publicfinalclassContentServiceextendsIContentService.Stub{ ...... publicstaticfinalclassObserverNode{ ...... privateStringmName; privateArrayList<ObserverNode>mChildren=newArrayList<ObserverNode>(); privateArrayList<ObserverEntry>mObservers=newArrayList<ObserverEntry>(); publicObserverNode(Stringname){ mName=name; } privateStringgetUriSegment(Uriuri,intindex){ if(uri!=null){ if(index==0){ returnuri.getAuthority(); }else{ returnuri.getPathSegments().get(index-1); } }else{ returnnull; } } privateintcountUriSegments(Uriuri){ if(uri==null){ return0; } returnuri.getPathSegments().size()+1; } publicvoidaddObserverLocked(Uriuri,IContentObserverobserver, booleannotifyForDescendents,ObjectobserversLock){ addObserverLocked(uri,0,observer,notifyForDescendents,observersLock); } privatevoidaddObserverLocked(Uriuri,intindex,IContentObserverobserver, booleannotifyForDescendents,ObjectobserversLock){ //Ifthisistheleafnodeaddtheobserver if(index==countUriSegments(uri)){ mObservers.add(newObserverEntry(observer,notifyForDescendents,observersLock)); return; } //Looktoseeiftheproperchildalreadyexists Stringsegment=getUriSegment(uri,index); if(segment==null){ thrownewIllegalArgumentException("InvalidUri("+uri+")usedforobserver"); } intN=mChildren.size(); for(inti=0;i<N;i++){ ObserverNodenode=mChildren.get(i); if(node.mName.equals(segment)){ node.addObserverLocked(uri,index+1,observer,notifyForDescendents,observersLock); return; } } //Nochildfound,createone ObserverNodenode=newObserverNode(segment); mChildren.add(node); node.addObserverLocked(uri,index+1,observer,notifyForDescendents,observersLock); } ...... } ...... } 从这里我们就可以看出,注册到ContentService中的ContentObserver按照树形来组织,树的节点类型为ObserverNode,而树的根节点就为ContentService类的成员变量mRootNode。每一个ObserverNode节点都对应一个名字,它是从URI中解析出来的。 在我们这个情景中,传进来的uri为"content://shy.luo.providers.articles/item",从Step 3调用mRootNode的addObserverLocked函数来往树上增加一个ObserverNode节点时,传进来的参数index的值为0,而调用countUriSegments("content://shy.luo.providers.articles/item")函数的返回值为2,不等于index的值,因此就会往下执行,而通过调用getUriSegment("content://shy.luo.providers.articles/item", 0)函数得到的返回值为"shy.luo.providers.articles"。假设这里是第一次调用树的根节点mRootNode来增加"content://shy.luo.providers.articles/item"这个URI,那么在接下来的for循环中,就不会在mRootNode的孩子节点列表mChildren中找到与名称"shy.luo.providers.articles"对应的ObserverNode,于是就会以"shy.luo.providers.articles"为名称来创建一个新的ObserverNode,并增加到mRootNode的孩子节点列表mChildren中去,并以这个新的ObserverNode来开始新一轮的addObserverLocked函数调用。 第二次进入到addObserverLocked函数时,countUriSegments("content://shy.luo.providers.articles/item")的值仍为2,而index的值为1,因此就会往下执行,这时候通过调用getUriSegment("content://shy.luo.providers.articles/item", 1)函数得到的返回值为"item"。假设这时候在以"shy.luo.providers.articles/item"为名称的ObserverNode中不存在名称为"item"的孩子节点,于是又会以"item"为名称来创建一个新的ObserverNode,并以这个新的ObserverNode来开始新一轮的addObserverLocked函数调用。 第三次进入到addObserverLocked函数时,countUriSegments("content://shy.luo.providers.articles/item")的值仍为2,而index的值也为2,因此就会新建一个ObserverEntry对象,并保存在这个以"item"为名称的ObserverNode的ContentObserver列表mObervers中。 最终我们得到的树形结构如下所示: mRootNode("") -- ObserverNode("shy.luo.providers.articles") --ObserverNode("item") , which has a ContentObserver in mObservers 这样,ContentObserver的注册过程就完成了。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/967018,如需转载请自行联系原作者

优秀的个人博客,低调大师

消息称字节跳动重启出售沐瞳科技,沙特公司或

彭博社援引知情人士消息称,字节跳动正在与沙特 Savvy 游戏集团洽谈,向后者出售旗下游戏公司上海沐瞳科技。 此次洽谈标志着字节跳动重启了出售沐瞳科技的计划,该计划在去年被搁置。字节跳动2021年以大约40亿美元的估值收购了沐瞳科技。沐瞳科技开发了在线战术竞技类游戏《无尽对决》。 知情人士表示,相关谈判仍在进行中,而且可能不会达成交易。 Savvy同样涉足电竞领域,由沙特公共投资基金在2021年创立。该基金一直致力于电子游戏领域的投资,旨在将沙特打造为行业中心。近日,该基金联合银湖资本及贾里德·库什纳(Jared Kushner)旗下Affinity Partners,以大约550亿美元收购美国EA公司。 字节跳动退出电子游戏领域的行动已持续一段时间。由于未能从竞争对手腾讯手中夺得市场份额,该公司不仅裁减了数百个岗位,还逐步停止了旗舰游戏品牌朝夕光年的运营。与此同时,随着流媒体和其它争夺用户的在线服务兴起,游戏开发商估值已从疫情时期的高点回落。 沐瞳科技成立于2014年。除了在东南亚地区广受欢迎的多人在线战术竞技游戏《无尽对决》外,该公司还开发了射击游戏《Project: Extraction》以及角色扮演游戏《晶核》。

优秀的个人博客,低调大师

思科创新峰会2017以技术为 迎接全数字化转型绝佳时机

017年3月24日,以“数字变革 创领世界”为主题的思科创新峰会2017在北京召开。开会的那天早晨,北京下起了绵绵春雨,“晓看红湿处,花重锦官城”的意境特别美。在思科创新峰会2017的主会场外,是思科技术团队和行业合作伙伴搭建的展厅,除了思科最前沿的技术展示之外,还有行业应用场景的再现,每一个展台前皆摩肩接踵人声鼎沸,如此热烈的讨论氛围,也很美。 思科创新峰会2017诚意十足,今年主题演讲囊括全数字化网络架构、数据中心、云、安全、协作、未来企业IT等众多方面,深入探讨了全数字化创新如何帮助企业重新定义商业模式,快速推动企业发展。 记者在会后采访了思科多位高层,通过他们的分享,记者对数字化转型的趋势判断、企业转型的痛点分析,以及技术如何支持企业转型又有了更深一层的理解,也发布出来,以飨读者。 迎接经济腾飞的第二次浪潮 思科全球副总裁,大中华区首席技术官曹图强首先在会上分享了自己对于全数字化转型的观点。他强调,现在是全数字化转型的绝佳时机,刻不容缓。 思科从何得出这个结论呢? 他认为首先是因为市场的需求:从全球到中国,众多传统企业都徘徊在转型路口,但此时的他们没有确定转型的方向,他们清楚地知道企业需要转型,但并不清楚该如何进行转型、用什么方式进行转型,这就是机遇。 此外从国家战略层面看,国家提出数字化、网络化、智能化的发展目标,作为执行力非常强的国家之一,有了国家的战略指导,大家都在向这个方向努力。“如果我们从现在开始真正实现了全数字化转型,我认为,这将是中国经济腾飞的第二次浪潮。” 曹图强表示。 由此不难看出,全数字化转型能给企业带来的优势和企业的急迫需求是不谋而合的,再加上国家战略的指导,现在确实是全数字化转型的绝佳时机。 那为什么思科认为转型“刻不容缓”?曹图强解释,因为商机随时可能被抢走。越来越多的企业都在寻找新的商业模式和新的创业机会。所以,如果抓不住瞬息万变的市场信息,无法确定全数字化转型的方向和策略,企业很可能错失良机。“全数字化转型的绝佳时机对于所有IT从业者、对于所有传统企业,的确是至关重要、刻不容缓的。” 为了证明思科对全数字化转型的信心和决心,曹图强给记者举了一个“Eating your own dog food(吃自己的狗粮)”的例子:思科IT部门利用思科自己研发的Tetration Analytics 平台加上AppDynamics进行分析后,把1/3的应用和35%的硬件从思科 IT的数据中心去掉了。思科本来打算在波士顿新建一个数据中心,但是通过分析发现,现有的数据中心完全可以支持思科的业务需求。由此可见精准的大数据分析可以为企业决策带来更智能的帮助。思科自己进行全数字化转型的成果,也意味着思科的研发方向,包括安全、智能、自动化,还能够真正帮助企业客户、合作伙伴来进行全数字化转型。 安全没有对与错 只分无效与有效 记者不是第一次采访思科大中华区副总裁,安全业务总经理庄敬贤了,可是这一次他在思科创新峰会2017会场非常忙碌:上午他在会场、展厅与合作伙伴、企业客户交流;下午“数字化时代 安全新生态”分会场他是第一位发言嘉宾,演讲主题为让安全助力企业创新”;随后在安全高峰圆桌上,他和思科的合作伙伴、客户坐在一起,讨论安全的应用与发展趋势,直到下午快结束时,记者才捕捉到他的身影。 庄敬贤首先给记者推荐了不久前新鲜出炉的《思科2017年度网络安全报告》,他表示这与大会全数字化转型的主题非常契合。他认为虽然全数字化转型确实为行业带来了很多机遇,但是同时也带来了前所未有的安全挑战。“随着互联网在速度、连网设备和流量方面不断增长,网络保护变得更复杂。企业转型中的挑战可以细分为三部分。”他指出,一是全数字化转型自然意味着受攻击面会持续扩大;二是攻击者借企业转型的商机牟取高额利润,活跃度大大增加;三是企业安全防御往往面临着人才匮乏、安全产品多,运营成本高但是有效性却不佳的窘境。 思科安全希望帮助客户解决上述三个安全挑战,策略就是提供集成架构防御,将安全技术集成起来,通过安全操作的简化提升企业的自动化。庄敬贤指出,思科的解决方案架构就是要解决安全产品过多、安全人才缺乏、安全有效性不高等问题。“需要强调的是,思科的集成不代表只是思科产品的集成,而是一个生态链的集成,包括安全事件、威胁情报、策略和上下游的集成,从而构建一个全面的集成的架构。” 正如创新峰会的名称一样,庄敬贤还和记者分享了思科在集成安全方面的创新案例,例如安全情报的共享:一位银行员工误点击链接下载了一个恶意软件,由于恶意软件第一次出现,可能思科的安全设备不能通过已知特征检测出来,但随后这个恶意软件会进入思科终端的恶意软件防护平台,如果还没有发现,就会被送至思科高级分析平台,在这个平台上,思科安全产品会发现这个软件把系统加密了,确定这是一个恶意软件。那么这些平台会马上报告思科统一的Talos安全情报中心,告诉Talos发现了新的恶意软件。Talos会第一时间把这个信息更新到互联网上所有的思科平台上,包括思科的防火墙、入侵防御、邮件安全、Web安全和软件防护等不同种类的平台上。更新完以后,还会把相应的地址,包括这个恶意软件的来源和域名更新到思科的DNS安全平台上。这意味着,当出现一个新的恶意软件,思科可以实现“一次发现,全球防护”,即只要发现了恶意软件,全球所有部署过思科网络安全平台的企业用户都可以对这些恶意软件进行防护。 此外思科在安全联动,策略共享方面也做了不小的创新:如果有恶意软件进入企业网络,思科新一代防火墙平台会进行智能分析和事件关联,得出有恶意软件入侵的结论。那么防火墙平台会立即通知思科的统一策略平台,通知连着交换机或者是连着无线的问题设备,把该设备直接踢下线或放在隔离区,让问题设备尽快去除病毒、进行恶意软件查杀。这是一个策略的联动,发现问题自动响应,比人为响应迅速很多。 记者了解到,思科安全目前是思科的核心业务之一。通过策略、威胁情报、信息和日志的共享,思科把所有不同的安全平台整合到一套统一的解决方案上,能够快速找到问题,解决问题,这就是思科集成的安全防御架构。庄敬贤表示,思科与业界合作伙伴构建生态链,希望未来可以在第三方的解决方案上实现共享、实现威胁情报上传策略。“我们最终的目标就是要建立简单、开放、自动、高效的思科安全平台。” 企业全数字化转型,思科甘做基石 思科大中华区高级副总裁,合作伙伴事业部总经理倪殿令形象地用盖房子来形容思科在全数字化转型中的定位:思科是全数字化转型的基石,思科将在基石上面去盖各式各样的、适合不同客户的、适应中国市场本地化的房子,这需要整个生态系统一起努力。 他表示,思科的合作伙伴生态系统,就是要把合作伙伴分类,帮助不同类型的、在不同阶段的合作伙伴转型。之前,思科以销售产品为主,是以产品为中心;现在,是以服务为中心,思科希望能够把客户关系从“买卖关系”转变成一种“全新的合作伙伴关系”。“最成功的转型,就是能够把一个客户做成合作伙伴,把一个合作伙伴做成客户。”他认为。在思科这个全数字化转型的基石上面,呈现的是思科+思科合作伙伴生态系统所代表的全数字化转型的成功。 思科大中华区副总裁,产品部总经理朱立新反问了记者一个问题:思科是一家什么样的公司?他认为,思科是一个硬件公司,也是一个软件公司,还是一个服务公司。在过去的几年中,思科在软件和服务如协作、安全上增加很多投入,一方面思科自主研发,另一方面思科积极收购优秀公司,增强自己服务基因。 朱立新还强调,今天物联网、大数据和云的发展下,软件不能像以前一样保持封闭式的开发形态。目前思科正在推动向软件、平台服务的转型,其变化一方面来自于产品技术和平台,另一方面来自于系统的开放性及可开发性。凭借对IP网络的深刻理解,思科正在创建下一代新型平台,允许用户和开发者调用网络能力,让软件、应用和网络及安全融为一体。在此过程中,思科提供大量的接口、开发环境、测试平台、培训平台及专属人员帮助开发者实现这一重大转变,思科DNA开发者大会及创新大赛是开启里程碑的第一步。 一天的采访下来,曹图强的一句话给记者留下深刻的印象:一家厂商的产品不可能满足所有的IT需求,这正是思科举办DNA开发者大会的初衷——希望通过生态系统为所有企业搭建一个他们需要的IT平台,在这个平台之上,所有客户能够和思科合作伙伴(特别是ISV合作伙伴)一起做二次开发,包括应用上的增值服务,这样才能真正为全数字化转型提供一个有效的平台。这也是思科多位高层共同传递出的一个愿景,记者也由衷地希望健康共赢的生态早日建成。 本文作者:周雪 来源:51CTO

优秀的个人博客,低调大师

Agent 落地四步法识别重复 → 拆能力 → 系统 → 养记忆

过去三个月,大厂把 Agent 从「概念」推到了「入口」:字节豆包工作、腾讯 WorkBuddy 开放平台、阿里千问办公、智谱把 Token 成本打下来 80%。 但我和十几个企业 IT、校园开发者聊下来,发现一个反常识的事实——大多数 Agent 项目不是死在模型不够强,而是死在「第一步就做错了」:把看起来重复的工作,当成了该自动化的工作。 这篇不聊概念,只聊工程化落地。我把可复用的方法拆成四步,每一步都给你反例和判断标准。

优秀的个人博客,低调大师

WorkBuddy DeepSeek 官方 API:自带 Key 配置实操,积分再也不见底

先说说我的痛点。我是才注册7天的 WorkBuddy 日常用户,在没摸熟络之前,一开始用的全是内置模型,结果积分掉得超级快——下午看还有 500 多,晚上就剩 200 多了。我一开始以为是系统出了问题,查了半天才发现:每次对话都在扣积分,尤其在处理长文档、多轮推理、复杂任务时掉的贼厉害,一天下来大几百积分说没就没了,对于新手尝试,一点也不“友好”。

优秀的个人博客,低调大师

OpenAI 员工套现潮达 30 亿美元,软银成最大“盘侠”

The Information报道指出,自2021年以来,人工智能巨头OpenAI的员工和前员工已通过多次股权出售,累计套现近30亿美元。对于这家成立仅六年的公司而言,如此大规模的员工财富变现实属罕见,甚至逼近埃隆·马斯克旗下SpaceX的员工套现水平。 报道披露,日本投资巨头软银已成为OpenAI员工股票的最大买家。今年春季,软银从一小批现任和前任员工手中收购了约2.4亿美元的股票。此前在今年1月,软银在OpenAI完成1500亿美元估值的66亿美元融资后,斥资15亿美元收购了现任和前任员工股票。综合历次交易,软银已买下近30亿美元员工套现股票中的约一半份额,成为这场估值游戏中最激进的玩家。 数据显示,OpenAI基本保持着每年两次的员工股票出售频率,这一节奏甚至超过了多数成熟科技公司。更值得关注的是员工的参与热情:在2021年8月的首次股票出售中,90%符合条件的员工选择以每股52美元的价格套现;到今年1月,仍有74%的符合条件员工以近210美元每股的价格出售股票。如今,随着公司最新一轮融资将估值推至2600亿美元,股权价格已超过每股250美元,不少员工或许在后悔当初的“早卖”,而市场也在期待公司是否会很快以新价格组织新一轮套现。 员工股权变现被许多CEO视为留住人才的关键工具,尤其是在OpenAI与Anthropic、xAI等AI初创公司,以及Meta Platforms等科技巨头之间激烈的人才争夺战中。然而,套现的另一面是可能加速员工离职——财富自由后,他们或许选择创业,甚至直接“退休”。 据报道,Anthropic和xAI等年轻公司也安排了大规模员工股票出售。AI云服务提供商CoreWeave也在2023年和2024年举行了两次大型二级股票出售,创始人和其他股东向富达、贝莱德和Coatue Management等投资者出售了总计13亿美元的现有股票。 OpenAI面临的竞争压力尤为严峻,前CTO Mira Murati创立的Thinking Machines Lab等新兴力量正在分流顶尖研究人才。股权套现虽然短期内缓解了员工的经济焦虑,但长期来看,能否留住核心团队,仍是公司必须直面的挑战。

优秀的个人博客,低调大师

日本政府称有朝鲜 IT 人员冒充日本人外包赚外汇

日本政府26日发布了一份提醒国内IT相关企业注意的文件,指出疑似有朝鲜IT技术人员冒充日本人,在网上承接IT相关业务订单。联合国等表示,为了赚取外汇用于核与导弹开发,朝鲜正在利用技术人员。据称,若把业务发包给此类人员,可能违反《外汇法》等。 文件由警察厅、外务省、财务省、经济产业省联名发布。美国和韩国去年10月发出了类似提醒,日本则是首次。警察厅干部透露称“在日本也发现了案例,威胁高涨”。 该文件等显示,朝鲜技术人员在为IT相关业务发包方和自由技术人员提供中介服务的网站上,或者伪造身份,或者通过住在日本的亲戚等进行注册。据称,他们大多在中国和俄罗斯工作并赚取报酬。其特点包括不参加视频会议、频繁变更收款账户、使用的日语不地道等。 在日本,神奈川县警方等6日以涉嫌违规领取失业补贴为由,逮捕了广岛县IT相关公司社长等人。警方认为其有可能把业务发包给了朝鲜技术人员,正在查明案情。神奈川县警方的调查中还发现,2022年,另外的朝鲜技术人员以熟人名义承接了兵库县防灾APP修改业务等。 延伸阅读:朝鲜 IT 开发者为美国公司从事远程外包工作

优秀的个人博客,低调大师

MakuBoot v2.3 发布,轻量级脚手架,私活的好帮手

介绍 maku-boot 是采用 SpringBoot、SpringSecurity、Mybatis-Plus 等框架,开发的一套 SpringBoot 快速开发平台,使用门槛极低,且采用 MIT 开源协议,完全免费开源,可免费用于商业项目等场景。 采用组件模式,扩展不同的业务功能,可以很方便的实现各种业务需求,且不会导致系统臃肿,若想使用某个组件,按需引入即可,反之亦然。 官网地址:https://maku.net 开发文档:https://maku.net/docs/maku-boot 演示环境:https://demo.maku.net/maku-boot 更新日志 新增参数管理,可动态配置系统参数 新增代码生成器模块,主要集成maku-generator 新增用户管理,Excel导入导出功能 优化定时任务,只允许执行指定类型的类 修复分栏布局展示问题 前端工程 Gitee 仓库:https://gitee.com/makunet/maku-admin Github 仓库:https://github.com/makunet/maku-admin 后端工程 Gitee 仓库:https://gitee.com/makunet/maku-boot Github 仓库:https://github.com/makunet/maku-boot 代码生成器 Gitee 仓库:https://gitee.com/makunet/maku-generator Github 仓库:https://github.com/makunet/maku-generator 架构图 效果图

优秀的个人博客,低调大师

- C3P0连池DeadLock机制分析

1 问题 近期,刚上线不久的生产系统的数据库连接池 C3P0 (版本为0.9.5.2)突然报出 APPARENT DEADLOCK!!! 错误。 1.1 错误日志 错误日志如下。 com.mchange.v2.async.ThreadPoolAsynchronousRunner$DeadlockDetector@7cf60134 -- APPARENT DEADLOCK!!! Creating emergency threads for unassigned pending tasks! com.mchange.v2.async.ThreadPoolAsynchronousRunner$DeadlockDetector@7cf60134 -- APPARENT DEADLOCK!!! Complete Status: Managed Threads: 3 Active Threads: 3 Active Tasks: com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask@3ce7f8aa on thread: C3P0PooledConnectionPoolManager[identityToken->1hgepsj9u1w3a4je54sly0|70165722]-HelperThread-#1 com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask@17f3c7a1 on thread: C3P0PooledConnectionPoolManager[identityToken->1hgepsj9u1w3a4je54sly0|70165722]-HelperThread-#2 com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask@65dc473c on thread: C3P0PooledConnectionPoolManager[identityToken->1hgepsj9u1w3a4je54sly0|70165722]-HelperThread-#0 Pending Tasks: com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask@744937e8 com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask@77e8b363 com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask@7eafacf0 com.mchange.v2.resourcepool.BasicResourcePool$ScatteredAcquireTask@79cde1be com.mchange.v2.resourcepool.BasicResourcePool$ScatteredAcquireTask@44067f18 com.mchange.v2.resourcepool.BasicResourcePool$ScatteredAcquireTask@76c7bf50 Pool thread stack traces: Thread[C3P0PooledConnectionPoolManager[identityToken->1hgepsj9u1w3a4je54sly0|70165722]-HelperThread-#0,5,main] java.net.SocketInputStream.socketRead0(Native Method) java.net.SocketInputStream.socketRead(SocketInputStream.java:116) java.net.SocketInputStream.read(SocketInputStream.java:171) java.net.SocketInputStream.read(SocketInputStream.java:141) oracle.net.ns.Packet.receive(Unknown Source) oracle.net.ns.DataPacket.receive(Unknown Source) oracle.net.ns.NetInputStream.getNextPacket(Unknown Source) oracle.net.ns.NetInputStream.read(Unknown Source) oracle.net.ns.NetInputStream.read(Unknown Source) oracle.net.ns.NetInputStream.read(Unknown Source) oracle.jdbc.driver.T4CMAREngine.unmarshalUB1(T4CMAREngine.java:1099) oracle.jdbc.driver.T4CMAREngine.unmarshalSB1(T4CMAREngine.java:1070) oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:478) oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:213) oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:796) oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1031) oracle.jdbc.driver.T4CPreparedStatement.executeMaybeDescribe(T4CPreparedStatement.java:836) oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1124) oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3285) oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3329) oracle.jdbc.OracleDatabaseMetaData.getTables(OracleDatabaseMetaData.java:2471) com.mchange.v2.c3p0.impl.DefaultConnectionTester$1.activeCheckConnectionNoQuery(DefaultConnectionTester.java:84) com.mchange.v2.c3p0.impl.DefaultConnectionTester$3.activeCheckConnectionNoQuery(DefaultConnectionTester.java:182) com.mchange.v2.c3p0.impl.DefaultConnectionTester.activeCheckConnection(DefaultConnectionTester.java:275) com.mchange.v2.c3p0.AbstractConnectionTester.activeCheckConnection(AbstractConnectionTester.java:79) com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.testPooledConnection(C3P0PooledConnectionPool.java:504) com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.testPooledConnection(C3P0PooledConnectionPool.java:464) com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.refurbishIdleResource(C3P0PooledConnectionPool.java:436) com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask.run(BasicResourcePool.java:2211) com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:696) Thread[C3P0PooledConnectionPoolManager[identityToken->1hgepsj9u1w3a4je54sly0|70165722]-HelperThread-#1,5,main] java.net.SocketInputStream.socketRead0(Native Method) java.net.SocketInputStream.socketRead(SocketInputStream.java:116) java.net.SocketInputStream.read(SocketInputStream.java:171) java.net.SocketInputStream.read(SocketInputStream.java:141) oracle.net.ns.Packet.receive(Unknown Source) oracle.net.ns.DataPacket.receive(Unknown Source) oracle.net.ns.NetInputStream.getNextPacket(Unknown Source) oracle.net.ns.NetInputStream.read(Unknown Source) oracle.net.ns.NetInputStream.read(Unknown Source) oracle.net.ns.NetInputStream.read(Unknown Source) oracle.jdbc.driver.T4CMAREngine.unmarshalUB1(T4CMAREngine.java:1099) oracle.jdbc.driver.T4CMAREngine.unmarshalSB1(T4CMAREngine.java:1070) oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:478) oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:213) oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:796) oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1031) oracle.jdbc.driver.T4CPreparedStatement.executeMaybeDescribe(T4CPreparedStatement.java:836) oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1124) oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3285) oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3329) oracle.jdbc.OracleDatabaseMetaData.getTables(OracleDatabaseMetaData.java:2471) com.mchange.v2.c3p0.impl.DefaultConnectionTester$1.activeCheckConnectionNoQuery(DefaultConnectionTester.java:84) com.mchange.v2.c3p0.impl.DefaultConnectionTester$3.activeCheckConnectionNoQuery(DefaultConnectionTester.java:182) com.mchange.v2.c3p0.impl.DefaultConnectionTester.activeCheckConnection(DefaultConnectionTester.java:275) com.mchange.v2.c3p0.AbstractConnectionTester.activeCheckConnection(AbstractConnectionTester.java:79) com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.testPooledConnection(C3P0PooledConnectionPool.java:504) com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.testPooledConnection(C3P0PooledConnectionPool.java:464) com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.refurbishIdleResource(C3P0PooledConnectionPool.java:436) com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask.run(BasicResourcePool.java:2211) com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:696) Thread[C3P0PooledConnectionPoolManager[identityToken->1hgepsj9u1w3a4je54sly0|70165722]-HelperThread-#2,5,main] java.net.SocketInputStream.socketRead0(Native Method) java.net.SocketInputStream.socketRead(SocketInputStream.java:116) java.net.SocketInputStream.read(SocketInputStream.java:171) java.net.SocketInputStream.read(SocketInputStream.java:141) oracle.net.ns.Packet.receive(Unknown Source) oracle.net.ns.DataPacket.receive(Unknown Source) oracle.net.ns.NetInputStream.getNextPacket(Unknown Source) oracle.net.ns.NetInputStream.read(Unknown Source) oracle.net.ns.NetInputStream.read(Unknown Source) oracle.net.ns.NetInputStream.read(Unknown Source) oracle.jdbc.driver.T4CMAREngine.unmarshalUB1(T4CMAREngine.java:1099) oracle.jdbc.driver.T4CMAREngine.unmarshalSB1(T4CMAREngine.java:1070) oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:478) oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:213) oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:796) oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1031) oracle.jdbc.driver.T4CPreparedStatement.executeMaybeDescribe(T4CPreparedStatement.java:836) oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1124) oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3285) oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3329) oracle.jdbc.OracleDatabaseMetaData.getTables(OracleDatabaseMetaData.java:2471) com.mchange.v2.c3p0.impl.DefaultConnectionTester$1.activeCheckConnectionNoQuery(DefaultConnectionTester.java:84) com.mchange.v2.c3p0.impl.DefaultConnectionTester$3.activeCheckConnectionNoQuery(DefaultConnectionTester.java:182) com.mchange.v2.c3p0.impl.DefaultConnectionTester.activeCheckConnection(DefaultConnectionTester.java:275) com.mchange.v2.c3p0.AbstractConnectionTester.activeCheckConnection(AbstractConnectionTester.java:79) com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.testPooledConnection(C3P0PooledConnectionPool.java:504) com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.testPooledConnection(C3P0PooledConnectionPool.java:464) com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.refurbishIdleResource(C3P0PooledConnectionPool.java:436) com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask.run(BasicResourcePool.java:2211) com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:696) Task com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask@65dc473c (in deadlocked PoolThread) failed to complete in maximum time 60000ms. Trying interrupt(). Task com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask@3ce7f8aa (in deadlocked PoolThread) failed to complete in maximum time 60000ms. Trying interrupt(). Task com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask@17f3c7a1 (in deadlocked PoolThread) failed to complete in maximum time 60000ms. Trying interrupt(). 上面的日志报出了 APPARENT DEADLOCK!!! 错误,然后输出了ThreadPoolAsynchronousRunner 的状态,包括线程数,当前正在处理的任务,队列中等待处理的任务,以及 ThreadPoolAsynchronousRunner 任务线程的堆栈。 1.2 配置 生产上的配置如下。 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name="driverClass" value="oracle.jdbc.driver.OracleDriver"></property> <property name="jdbcUrl" value="${datasource.jdbcUrl}"></property> <property name="user" value="${datasource.user}"></property> <property name="password" value="${datasource.pass}"></property> <property name="maxPoolSize" value="${datasource.maxPoolSize}"></property> <property name="maxIdleTime" value="180"></property> <property name="idleConnectionTestPeriod" value="60"></property> <property name="checkoutTimeout" value="30000"></property> </bean> 基本相当于使用默认配置。 1.3 初步判断 根据日志,C3P0判断其线程池(ThreadPoolAsynchronousRunner)中的任务出现死锁(DEADLOCK),此时线程处于检查空闲数据链接状态是否可用。单从日志字面暂时无法判断问题所在。 查阅了网上的资料,对此问题原因分析较少,绝大部分不甚了了。比较多的文章是尝试通过调整参数来避免问题的出现的帖子。 秉着 “知其所以然” 的态度,再加上一点点的好奇心,我翻阅了一下C3P0的代码,试图从中发现端倪。 2 C3P0的死锁(DeadLock)机制分析 2.1 C3P0类结构 为了后面展示代码的需要,先附上一个我整理的C3P0的类结构图,有助于我们分析问题时确定问题的位置。如果下面的图太小,可以点击这里浏览 c3p0类结构图 我们重点关注右下角的几个类。 2.2 ThreadPoolAsynchronousRunner线程池 在 C3P0PooledConnectionPoolManager 类在初始化时中创建了一个taskRunner,其类型为 ThreadPoolAsynchronousRunner , 是一个 C3P0 自定实现的线程池。 public final class C3P0PooledConnectionPoolManager{ ... ThreadPoolAsynchronousRunner taskRunner; ... private synchronized void _poolsInit() { ... this.taskRunner = createTaskRunner( num_task_threads, matt, timer, idStr + "-HelperThread" ); ... } ... private ThreadPoolAsynchronousRunner createTaskRunner( int num_threads, int matt /* maxAdministrativeTaskTime */, Timer timer, String threadLabel ) { ... out = new ThreadPoolAsynchronousRunner( num_threads, true, timer, threadLabel ); return out; ... } ... } ThreadPoolAsynchronousRunner 线程池被作为成员注入到多个对象之中,最终用于执行 BasicResourcePool 类中定义的 ScatteredAcquireTask, AcquireTask, RemoveTask, DestroyResourceTask, RefurbishCheckinResourceTask, AsyncTestIdleResourceTask 等任务。 我们来看一下 ThreadPoolAsynchronousRunner 的代码。 public final class ThreadPoolAsynchronousRunner implements AsynchronousRunner { ... TimerTask deadlockDetector = new DeadlockDetector(); ... public ThreadPoolAsynchronousRunner( int num_threads, boolean daemon, Timer sharedTimer, String threadLabel ) { this( num_threads, daemon, DFLT_MAX_INDIVIDUAL_TASK_TIME, DFLT_DEADLOCK_DETECTOR_INTERVAL, DFLT_INTERRUPT_DELAY_AFTER_APPARENT_DEADLOCK, sharedTimer, false, threadLabel ); } ... private ThreadPoolAsynchronousRunner( int num_threads, boolean daemon, int max_individual_task_time, int deadlock_detector_interval, int interrupt_delay_after_apparent_deadlock, Timer myTimer, boolean should_cancel_timer, String threadLabel ) { this.num_threads = num_threads; this.daemon = daemon; this.max_individual_task_time = max_individual_task_time; this.deadlock_detector_interval = deadlock_detector_interval; this.interrupt_delay_after_apparent_deadlock = interrupt_delay_after_apparent_deadlock; this.myTimer = myTimer; this.should_cancel_timer = should_cancel_timer; this.threadLabel = threadLabel; recreateThreadsAndTasks(); myTimer.schedule( deadlockDetector, deadlock_detector_interval, deadlock_detector_interval ); } ... } 可以看到, ThreadPoolAsynchronousRunner 线程池默认创建3个执行线程,并且创建了一个 死锁检测(DeadLockDetector) 的定时任务,默认情况下每10秒执行一次。 2.3 DeadLockDetector内部机制 public final class ThreadPoolAsynchronousRunner implements AsynchronousRunner { ... class DeadlockDetector extends TimerTask { LinkedList last = null; LinkedList current = null; public void run() { ... current = (LinkedList) pendingTasks.clone(); ... if ( current.equals( last ) ) { //System.err.println(this + " -- APPARENT DEADLOCK!!! Creating emergency threads for unassigned pending tasks!"); ... recreateThreadsAndTasks(); run_stray_tasks = true; } ... if (run_stray_tasks) { AsynchronousRunner ar = new ThreadPerTaskAsynchronousRunner( DFLT_MAX_EMERGENCY_THREADS, max_individual_task_time ); for ( Iterator ii = current.iterator(); ii.hasNext(); ) ar.postRunnable( (Runnable) ii.next() ); ar.close( false ); //tell the emergency runner to close itself when its tasks are complete last = null; } else last = current; ... 可以看到,DeadLockDetector是ThreadPoolAsynchronousRunner 的内部类,是一个默认每10秒执行一次的定时任务。其工作原理是定时检查任务队列(pendingTasks)中的任务,如果前后两次检查发现待执行的任务没有变化,就认为可能产生了死锁,并额外创建线程执行等待任务。 3 问题分析 3.1 线程等待 从代码中可以看出,APPARENT DEADLOCK!!!报错原因主要是ThreadPoolAsynchronousRunner线程池的中的等待任务队列在经过一定时长(默认10秒)前后没有变化。 结合问题日志。我们可以初步判断问题的原因,是由于某些任务(如日志中显示AsyncTestIdleResourceTask)执行过程中失去响应(超过60s没有收到响应),导致后续的任务长时间等待,进而报错。 3.2 具体分析 进而观察执行线程的堆栈,可以发现三个任务都在执行检测连接有效性时,向服务端发送请求数据后,Oracle数据库未返回响应,导致线程长时间等待。 由于此问题无法通过构造场景重现,只能猜测,产生这种原因可能是连接在服务端已经超时,由服务端主动关闭连接,但由于TCP协议断开连接需要完成4次握手,而服务端只完成了前两次握手,这样就导致了客户端可以发送消息但接收不到任何内容,长时间等待直到超时。 3.3 解决方案 根据以上分析,我认为问题是maxIdleTime(连接最大空闲时间)与idleConnectionTestPeriod(空闲检测周期)参数配置与数据库不匹配引起的。 根据DBA的建议,将maxIdleTime调整为30秒,将idleConnectionTestPeriod调整为10秒。 4 后续 调整完后系统稳定运行,至今未再报出同样的错误。 在查看资料的过程中,发现有与我类似的情况,请看这里。问题出在同一块代码,但现象与原因均与我不同。我并不认为这是C3P0的缺陷,而是具体环境上的配置没有相匹配。 我再说一点废话,C3P0中大量使用了synchronized关键字进行加锁,由于单例没有使用double-check,导致正常代码中大量加锁,在高并发下效率很低。因此,如果性能对系统较为重要,还是推荐大家使用HikariCP替换C3P0。

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册