}
}
}
}
之前一个项目部署后访问抛出异常ConcurrentModifiedException ,看代码后发现有段逻辑中用到了一个处理集合的地方,逐个删除不符合条件的DO。
使用的是list.remove();
这个方法用在list的迭代中会抛出ConcurrentModificationException异常,随换用Iterator自带的iterator.remove()方法后安全执行.
| Iterator<ShopStreetDO> iterator = streetList.iterator();
iterator.remove();
|
虽然这个貌似很小的点而已,但往往在系统中却是个隐藏的大bug.
原因:
有意思的是如果你的 Collection / Map 对象实际只有一个元素的时候, ConcurrentModificationException 异常并不会被抛出。这也就是为什么在 javadoc 里面指出: it would be wrong to write a program that depended on this exception for its correctness: ConcurrentModificationException should be used only to detect bugs |
针对项目中遇到的那种用法的性能差别:
| remove方法由于牵涉到集合里单向列表索引的移动,所以性能很低。如若集合大小超过100个,迭代remove次数频繁的话就要考虑避开这种方式了。可以考虑用add,新建一个list,把符合条件的对象add进来。这样性能提高很多。随着remove频率的逐渐升高,两者的性能差别会是数量级的。集合大小100个之内,移动次数不频繁的话,add方式和remove方式性能差不多。
|
大家可以做个试验:
这段代码是个反例,不要学习。
| String str1 = new String(“test”); String str2 = new String(“test”); String str3 = new String(“test”); String str4 = new String(“test”); String str5 = new String(“test”); List list = new ArrayList(); list.add(str1); list.add(str2); list.add(str3); list.add(str4); list.add(str5); System.out.println(“list.size()=” + list.size()); for (int i = 0; i < list.size(); i++) { list.remove(i); System.out.println(“after list.size()=” + list.size()); } System.out.println(“last list.size()=” + list.size()); |
解决的方法:
方法1:倒过来遍历list
| for (int i = list.size()-1; i > =0; i–) {
list.remove(i); }
|
方法2:每移除一个元素以后再把“指针”i移回来
| for (int i = 0; i < list.size(); i++) {
list.remove(i); i=i-1; }
|
方法3:使用iterator.remove()方法
| for (Iterator it = list.iterator(); it.hasNext();) {
String str = (String)it.next(); it.remove(); }
|
梅竹山庄→西溪梅墅→西溪水阁→秋雪庵
最美妙的当然还是坐在摇橹船上赏景,这里的船老大会自豪地告诉你:葛优坐的是哪艘船,舒淇和方中信坐的又是哪艘船。由着船工的性子从内河道进发,周家码头——梅竹山庄——西溪梅墅——西溪水阁——秋雪庵——周家码头,游玩西溪,不可太贪心,想一次游个遍,需水陆结合,慢慢晃荡。
掩映在茂林深处的西溪水阁是文人雅士们隐居、读书的地方,由两组建筑组成,东为“拥书楼”,是文人居所;西为“蓝溪书屋”,是藏书读书之地。
位于中心水域的秋雪庵是西溪最美的地方,四面环水,只有靠划桨小船才能进入。“西溪芦”曾是清代西湖18景之一,而秋雪庵就是西溪观芦的最佳处。坐船靠近秋雪庵时,一群群野鸭会神气地从芦苇丛中钻出,横穿过“河路”,消失在另一片芦苇荡中。
顺便说一句,湿地公园附近有一个免费的观赏区,那里有座小塔,登上塔顶便能一览湿地风光,别有一番滋味。
如果选择步行,可以沿这条线行走:周家村(园区主入口)——梅竹山庄——泊庵草堂——烟水渔庄——小姑桥——深潭口——深潭港西侧游步道——虾龙滩保护区——西溪水阁——西溪梅墅,全程约8公里,需时3个半小时以上。
如何让一个UED前端同学跟你快乐的合作,方式有很多种,我先分享其中一种:其实后端同学跟他们一起完成功能需求的时候,尤其是时间非常短的紧急需求,你只要多花点时间帮他们理一下需求和写一些文档给他们。 比如拿我工作中的列子:
第一种场景, 打开活动投票页面的时候:
实现方式:前端ued同学采用JSONP的方式发起请求后端300条数据,然后在每个分类中展示对应的100条数据。
请求连接:隐私
服务器返回数据JSON格式:darenCallback(约定好的函数名)({
“meifu”:[ {"type":"meifu","nick":"可爱的小娃娃",...},{"type":"meifu","nick":"齐B小短裙",...},.... ],
“meiru”:[ {"type":"meiru","nick":"可爱的小娃娃2",...},{"type":"meiru","nick":"齐B小短裙2",...},.... ] ,
“meiti”:[ {"type":"meiti","nick":"可爱的小娃娃3",...},{"type":"meiti","nick":"齐B小短裙3",...},.... ]
})
PS:
1,后端直接返回数据给前端时,把300条数据先分类好。比如json格式”美肤”:100条数据,”美汝”:100条数据,”美体”:100条数据。方便前端分类tab切换数据。
2,在返回的对象中我用省略号省去了一些熟悉,具体的字段属性和字段含义如下:
type:达人所属分类
nick:达人报名的昵称(这个不是达人的旺旺nick,防止达人被骚扰)
userId:达人的旺旺数字id(此属性在页面上不展示,前端同学会放在对应达人的隐藏域中,后续的观察投票和关注达人组件需要用到)
source:达人来源 (如新浪推荐)
num: 达人编号
peoPicurl: 达人图片
expertScore:评委对达人的评分数
vote:达人的得票数
followers:达人的粉丝数
itemPicurl:达人推荐的商品大图
itemTitle:达人推荐的商品名字
itemReason:达人推荐的商品理由
spulist:达人推荐的该商品对应的SPU list
第二种场景, 进入投票页面的观众选择给某达人投票的时候:
实现方式:前端ued同学采用JSONP的方式发起请求美容后端服务器
请求连接:隐私
服务器返回数据JSON格式:
1,正常返回:voteCallback(约定好的函数名)){
“message”:”投票成功”,”vote”,11111}其中vote为当前达人的最新得票数
或者voteCallback{
“message”:”抱歉!您今天已经投过票了,请明天再来吧!”}
或者voteCallback{
“message”:”抱歉!经由您的计算机今天发起的投票已经超过上限,请明天再来吧!”}(这个后台技术实现还未定)
2,异常返回:voteCallback{
“message”:”抱歉!本次操作由于系统繁忙或者未知错误,没有成功 , 请稍后重试!”}
PS:
请求连接中的?号后面参数需要UED同学从页面中带过来,其中viewUserId为进入该投票页面的观众淘宝账号(即旺旺账号)数字id
(前端同学可以从cookie中获取);userId为当前达人的淘宝账号数字id(前端同学可以从当前达人的隐藏域中获取,这个在第一种场景中有特意提到)
第三种场景, 观众关注达人的时候:
实现方式:前端ued同学采用JSONP的方式发起请求美容后端服务器获取达人的最新粉丝数
请求连接:隐私
服务器返回数据JSON格式:
1,正常返回:guanzhuCallback(约定好的函数名)){
“followers”,11111}其中followers为当前达人的最新得票数
或者guanzhuCallback(约定好的函数名)){
“followers”,”保密”}由于达人的安全设置等因素无法取到粉丝数,这个运营同学们会告诉达人们都放开
2,异常返回:guanzhuCallback{
“message”:”抱歉!本次操作由于系统繁忙或者未知错误,没有成功 , 请稍后重试!”}
PS:
1,第三种场景其实有2个异步交互,首先是用SNS关注组件请求SNS后端服务器(这个组件作者已经封装好逻辑)返回一些数据;
其次是在异步请求美容后端服务器获得达人粉丝数来动态更新
2,第三种关注达人的异步交互场景主要是保证加关注达人这个SNS组件功能OK,如果在其次请求美容后端服务器隐私
,假设服务器异常返回,那么不要在页面上提示这个异常信息来迷糊观众,造成用户体验很差。优先保证正常关注功能变成已关注状态(至于在异常特殊情况粉丝数下不能加1相比不是很重要)
埋点方案:http://wiki.ued.taobao.net/doku.php?id=tms:spm%E5%9F%8B%E7%82%B9%E6%96%B9%E6%A1%88
埋点功能和指标介绍:http://dw.taobao.ali.com/klc/baike/baikeInfo.htm?id=30&ticket=53fde5ba-78b6-413f-a883-4d4327eb7837
埋点技术接口人:
对埋点原理的理解:
1线上Apache新增了一个模块会生产埋点的JS(atp.js)(daily环境下需通知联系系统同学告知SCM配置和修改taobaoBeacon.cfg,要不然不会加载),当浏览器渲染完后会去发请求这个JS,这个JS处理逻辑把标签中的meta子标签的a(表是站点).b(表示站点下的某页面)值推送到埋点服务器来统计当前a站点下的b页面PV
2当浏览器加载完整个DOM结构后,该apt.js把<body>体里面的容器根节点(div,p等)的能发起http请求的子节点(a,img等)的c(模块).d(模块中的某位置)的值推送到埋点服务器来采集点击和引导效果。
对这个后续的安排:上述apt.js的原理目前还不适合我们很多系统后端java对应的VM文件。我们写的大部分screen 主题页面VM一般都布局在layout下的default.vm中,而meta元信息只能写在default.vm中head中,那么原理1中介绍的就不能写a.b属性值,有一种解决方式就是为每一个主题VM页面都写一个对应的
default.vm(这个不太现实,要改动已有页面的分类同时要担心其他系统对这个页面的引用改动,而且
以后开发新页面都要建一个新的default.vm,这个约束太大放弃。)
把VM的这个情况跟负责埋点的同学一起当面沟通了一下和讨论技术后,目前达成的意见是:
1,他们那边第一步尽快修改atp.js,兼容VM的情况:在meta中我们只写a(站点)的值,剩下的
b.c.d的值写在body里的容器中,这样对埋点原理理解中的第二个同样支持,同时去掉原来spm.js的引用
2,由于第一步的解决方案不能满足window onload后当前页面PV的统计,打算第二步调整
atp.js把当前页面的http地址(不包括参数)推送到埋点服务器,采集a+URL的方式来统计当前站点下的页面PV统计.(这种方案唯一担心的事,后端系统的域名和页面名字会不要变化太频繁)。
3,了解系统已有旧埋点方式,等他们至少第一步完成后,我们开始修改已有系统埋点旧方式而采用他们这个统一埋点方式。
欢迎使用 WordPress。这是系统自动生成的演示文章。编辑或者删除它,然后开始您的博客!