2007年4月27日

新写了一个Java并发程序设计教程, 用于公司内部培训的,和2007年写的那个相比,内容更翔实一些。

内容列表

1、使用线程的经验:设置名称、响应中断、使用ThreadLocal
2、Executor :ExecutorService和Future ☆ ☆ ☆
3、阻塞队列 : put和take、offer和poll、drainTo
4、线程间的协调手段:lock、condition、wait、notify、notifyAll ☆ ☆ ☆
5、Lock-free: atomic、concurrentMap.putIfAbsent、CopyOnWriteArrayList ☆ ☆ ☆
6、关于锁使用的经验介绍
7、并发流程控制手段:CountDownlatch、Barrier
8、定时器: ScheduledExecutorService、大规模定时器TimerWheel
9、并发三大定律:Amdahl、Gustafson、Sun-Ni
10、神人和图书
11、业界发展情况: GPGPU、OpenCL
12、复习题

下载地址:

 http://files.cnblogs.com/jobs/Java%e5%b9%b6%e5%8f%91%e7%a8%8b%e5%ba%8f%e8%ae%be%e8%ae%a1%e6%95%99%e7%a8%8b.pdf  

 欢迎看了之后写反馈给我。
博客园的文章地址:

http://www.cnblogs.com/jobs/archive/2010/07/29/1788156.html

posted @ 2010-07-30 00:41 温少的日志 阅读(6386) | 评论 (12)编辑 收藏
 
Google云计算AppEngine Java版刚刚推出来的时候,我就申请了该服务。该服务的申请需要提供手机号码验证,GOOGLE很牛B,能够发送全球的手机短信。申请的帐号放了很久, 前段时间学习OpenID,需要作一个范例,于是就在Google AppEngine上作,作的过程发现其不能使用线程,导致HttpClient组件无法工作,于是我修改了OpenID4Java的实现,全部使用 URLConnection来实现。最终程序部署成功了,网址 http://cogito-study.appspot.com,欢迎大家测试使用。

我来说一下我对Google AppEngine Java版本的使用感受吧。
1、 Google AppEngine Java版本,具备基本功能,但是由于缺乏一些重要的功能,例如线程,没有线程,很多库无法使用,例如我上面提到的HttpClient不能使用。 Google提供一个类的白名单http://code.google.com/intl/zh-CN/appengine/docs/java /jrewhitelist.html,大多数需要使用的类都有,javax.xml.crypto不再其中,使得我要部署一个SAML2的实现时玩不 转。
2、Google AppEngine提供了一个DataStore,使用JDO访问数据,其查询语言支持GQL。基本功能是具备的,但是也是存在很大的局限性,最多返回 1000行数据,COUNT(*)也是最多返回1000行。这个限制使得很多应用要跑在其上,会很麻烦。
3、部署很简单,在Eclipse中使用Google提供的插件,输入帐号密码就可以部署了,太简单了。但我使用的过程中,经常出现某些时段无法部署的情况,通常遇到这种情况,多尝试几次或者过段时间再尝试就好了。
4、管理界面简洁方便,功能基本完备。包括性能监控、数据管理、日志、计费等都有。
总结
Google的AppEngine Java版本已经具备了基本功能,可以部署简单应用了,但是由于其功能不够完备,目前大多数应用要部署在其上,都会要做相当大的修改或者无法实现。
posted @ 2009-09-27 20:29 温少的日志 阅读(1508) | 评论 (0)编辑 收藏
 
我在Google AppEngine上部署了一个Java应用(OpenID测试)
http://cogito-study.appspot.com

Google Apps不支持线程,所用到的库openid4java需要创建线程(因为HttpClient),我修改了openid4java的实现,使得其支持Google App Engine。

部署在Google App Engine上的应用可以应用任何OpenID Provider登陆,包括Google、Yahoo、MSN等。

你可以通过这个测试网站了解OpenID
posted @ 2009-09-24 21:22 温少的日志 阅读(1638) | 评论 (1)编辑 收藏
 
最近花了较多时间学习单点登陆以及相关的安全技术,做一个简单的总结,发表我的一些看法。抛砖引玉,希望各位朋友参与讨论。

单点登陆,鸟语原文为Single Sign-On,缩写为SSO。别以为单点登陆是很时髦高深的技术,相反单点登陆是很古老的技术,例如1980年kerberos v4发布,一直发展至今,被Windows、Mac OS X、Linux等流行的操作系统所采用,是为应用最广泛的单点登陆技术。

kerberos适用于局域网,十分成熟。互联网发展之后,多个网站需要统一认证,业界需要适合互联网的单点登陆技术,也就是WEB SSO。2002年,微软提出了passport服务,由微软统一提供帐号和认证服务,理所当然,大家都不愿意受制于微软,但是很认同微软提出WEB SSO理念,于是产生了Liberty Alliance,另外指定一套标准,这套标准发展起来就是SAML,目前SAML的版本是SAML V2,属于OASIS的标准。

--------------
SAML

SAML,鸟语全名为Security Assertion Markup Language,弥漫着学院派的腐尸味道,缩写十分怪异,令人望而生畏。计算机行业,向来崇尚时髦,SAML这一名称使得其较少受到大众程序员的关注。

SAML的标准制定者,来自SUN、BEA、IBM、RSA、AOL、Boeing等大公司,制定技术规范相当专业有水准,系统分层合理,抽象了几个概念把整个系统描述得很清楚,使用流行技术XML Schema来描述协议,使用到了XML-Sign和XML Encrypt等较为前缘XML安全技术。

SAML的基本部分包括Protocol、Bingding、Profile、Metadata、AuthenticationContext。其中Protocol是交互消息的格式,例如AuthnRuequest/Response(认证请求/响应)的消息对。Bingding是指协议所采用的传输方式,例如使用HTTP Redirect或HTTP POST或SOAP的方式传输Protocol中所定义的消息。Profile是系统角色间交互消息的各种场景,例如Web Single Sign-ON是一种Profile、Single Sign-Out也是一种Profile、身份联邦也是一种Profile。各个参与方所提供的服务的描述信息为metadata。系统的认证方法通常是千差万别的,AuthenticationContext是SAML中定义的认证扩展点,可以是最普通的User Password认证,也可以是kerberos认证,也可以是电信常用的RADIUS,或者是动态密码卡。

SAML在Java企业应用中,得到广泛支持,IBM、BEA、ORACLE、SUN的Java应用服务器都提供了SAML的支持,曾经有人说,SAML就是如同JDBC一样,将会是使系统集成的准入证。SAML有很多开源实现,包括SUN公司的Open SSO,不幸的是,这些开源实现都不够好,或者相当糟糕,如果我们需要支持SAML协议,可能需要在开源的版本上裁剪或者另行开发。

SAML考虑了Web SSO,也考虑了传统的SSO集成,包括Kerberos和LDAP的集成,其中Attributed扩展机制以及相关规范,使得SAML拥有良好的扩展性,很好集成传统协议和支持新协议。

SAML是一个定义良好的规范,概念清晰,分层合理,扩展性良好,一切都很棒,但是有一点不好,就是曲高和寡!

-------------
OpenID
有一些互联网公司,拥有众多很多帐号,很牛B,例如GOOGLE、YAHOO、Facebook,希望别人的系统使用它们的帐号登陆。他们希望一种足够简单的WEB SSO规范,于是选择一种草根网络协议OpenID。OpenID,名字取得好,顾名思义,一看就知道它是干嘛的。国内也有它的Fans,例如豆瓣网。openID的确足够简单,但是协议本身是不完善,可能需要一些补充协议才能够满足业务需求。例如GOOGLE采用OpenID + OAuth。目前支持OpenID有Yahoo、Google、Windows Live,还有号称要支持OpenID的Facebook。目前Yahoo和Google宣称对OpenID的支持,但是其实是有限制的,Yahoo的OpenID只有少数合作伙伴才能获得其属性,Google也只有在其Google Apps中才能获得账号的Attribute。用户账号毕竟是一个互联网公司的最宝贵资源,希望他们完全分享账号是不可能的。

Open ID和SAML两种规范,都将会减少系统间交互的成本,我们提供Open API时,应该支持其中一种或者或两种规范。

--------------
OAuth

oAuth涉及到3大块的交互和通信。1. 用户,2. 拥有用户资料/资源的服务器A,3. 求资源的服务器B,。

oAuth的典型应用场景(senario)
以前,用户在 拥有资源 的的网站A有一大堆东西;现在用户发现了一个新的网站B,比较好玩,但是这个新的网站B想调用 拥有资源的网站A的数据。

用户在 求资源的网站B 上,点击一个URL,跳转到 拥有 资源的网站A。
拥有资源的网站A提示:你需要把资源分享给B网站吗?Yes/No。
用户点击 Yes,拥有资源的网站A 给 求资源的网站B 临时/永久 开一个通道,然后 求资源的网站 就可以来 拥有资源的网站 抓取所需的信息了。
(参考资料:http://initiative.yo2.cn/archives/633801)
(摘抄)
--------------

内部系统间集成使用LDAP、Kerberos,外部系统集成使用SAML或者OpenID + OAuth,这是一种建议的模式。

------------
PAM

人们寻找一种方案:一方面,将鉴别功能从应用中独立出来,单独进行模块化设计,实现和维护;另一方面,为这些鉴别模块建立标准 API,以便各应用程序能方便的使用它们提供的各种功能;同时,鉴别机制对其上层用户(包括应用程序和最终用户)是透明的。直到 1995 年,SUN 的研究人员提出了一种满足以上需求的方案--插件式鉴别模块(PAM)机制并首次在其操作系统 Solaris 2.3 上部分实现。插件式鉴别模块(PAM)机制采用模块化设计和插件功能,使得我们可以轻易地在应用程序中插入新的鉴别模块或替换原先的组件,而不必对应用程序做任何修改,从而使软件的定制、维持和升级更加轻松--因为鉴别机制与应用程序之间相对独立。应用程序可以通过 PAM API 方便的使用 PAM 提供的各种鉴别功能,而不必了解太多的底层细节。此外,PAM的易用性也较强,主要表现在它对上层屏蔽了鉴别的具体细节,所以用户不必被迫学习各种各样的鉴别方式,也不必记住多个口令;又由于它实现了多鉴别机制的集成问题,所以单个程序可以轻易集成多种鉴别机制如 Kerberos 鉴别机制和 Diffie - Hellman 鉴别机制等,但用户仍可以用同一个口令登录而感觉不到采取了各种不同鉴别方法。PAM 后来被标准化为 X/Open UNIX® 标准化流程(在 X/Open 单点登录服务(XSSO)架构中)的一部分。(摘抄)

如果我们设计一个认证系统,PAM是应该参考借鉴的。

-------------
JAAS
Java Authentication Authorization Service(JAAS,Java验证和授权API)提供了灵活和可伸缩的机制来保证客户端或服务器端的Java程序。Java早期的安全框架强调的是通过验证代码的来源和作者,保护用户避免受到下载下来的代码的攻击。JAAS强调的是通过验证谁在运行代码以及他/她的权限来保护系统面受用户的攻击。它让你能够将一些标准的安全机制,例如Solaris NIS(网络信息服务)、Windows NT、LDAP(轻量目录存取协议),Kerberos等通过一种通用的,可配置的方式集成到系统中。在客户端使用JAAS很简单。在服务器端使用JAAS时情况要复杂一些。(摘抄)

-------------
Spring Security,Spring框架大名鼎鼎,Spring Security属于SpringFramework旗下的一个子项目,源自acegi 1.x发展起来。很多人项目应用了spring security,但我个人看来,spring security绝对不算是一个设计良好的安全框架。其设计感觉就是一个小项目的安全认证实践罢了。

-------------
CAS
应用最广的开源单点登陆实现了,其实现模仿Kerberos的一些概念,例如KDC、TGS等等,都是来自于Kerberos。CAS对SAML和OpenID协议支持得不够好。个人感觉类似Kerberos的机制在互联网中可能过于复杂了。我感觉单纯的ticket机制,过于局限于基于加密解密的安全了,感觉上SAML的Assertion概念更好,Assertion可以包括认证、授权以及属性信息。

-------------


--------------------------
09博客园纪念T恤
新闻:Wordpress发布实时RSS技术 推动实时网络发展
网站导航: 博客园首页  个人主页  新闻  社区  博问  闪存  找找看
posted @ 2009-09-09 01:17 温少的日志 阅读(653) | 评论 (1)编辑 收藏
 
现在很多开源项目在使用LOG的时候做了不好的示范--在基类中实例化的方式使用LOG,而不是静态变量。

例如:

class Base  {
     private final Log LOG = LogFactory.getLog(this.getClass());
}

class Derived  {
    public void foo() {
           if (LOG.isDebugEnabled()) LOG.debug("foo");
    }
}

这种用法,当类被继承的时候,LOG就完全乱了。spring、struts都有这样的问题。

正确的使用方式应该是直接静态化声明LOG。

例如:

class DerivedA  {
     private final static Log LOG = LogFactory.getLog(DerivedA.class);
}



--------------------------
盛大招聘.Net开发工程师
经典好书:.NET框架程序设计(修订版)
新闻:2008年最精彩科技图片:电流运动模拟图居首
导航:博客园首页  知识库  新闻  招聘  社区  小组  博问  网摘  找找看
文章来源:http://www.cnblogs.com/jobs/archive/2009/01/05/1368894.html
posted @ 2009-01-05 10:49 温少的日志 阅读(2450) | 评论 (13)编辑 收藏
 
Eclipse包含很多插件,插件之间有复杂的依赖关系,如果使用单独下载安装的方式,容易遗失部分需要依赖的插件。

在Ecliipse的Software Update功能中安装插件,能够解决插件依赖的问题,但是在Eclipse 3.4之前的版本,Software Update不能够多线程同时下载,遇到网速较慢的更新站点时,需要漫长的等待,有时候安装一个插件,需要数个小时,甚至更久。

在Eclipse 3.4之后,Software Update有了很大的改变,可以多线程下载了,但是不能手工选择镜像,它会笨笨的选择一些很慢的站点,效果变得更差了,下载速度时快时慢,但是经常都比以前手工选择镜像要慢。经常选择一些只有数百B速度的下载站点,令人抓狂!

所以说,Eclipse 3.4的Software Update功能依然令失望。

期待数年,终于盼来了新版的Software Update功能,但是新版的更差了,哎。。。

posted @ 2008-07-10 03:49 温少的日志 阅读(3633) | 评论 (8)编辑 收藏
 
     摘要: 我们在开发中,经常需要遍历一个目录下的所有文件,常用的办法就是使用一个函数递归遍历是常用的办法。但是递归函数的缺点就是扩展不方便,当然你对这个函数加入一个参数FileHandler,这样扩展性稍好一些,但是仍然不够好,比如说,不能根据遍历的需要中途停止遍历,加入Filter等等。我实现了一个FileIterator,使得遍历一个目录下的文件如何遍历一个集合中的元素一般操作。  阅读全文
posted @ 2008-06-05 07:56 温少的日志 阅读(1867) | 评论 (2)编辑 收藏
 

http://openjdk.java.net/上的Announcements:

2008/04/28 New Project approved: More New I/O APIs for the Java Platform

包括内容:
  • 4313887 New I/O: Improved filesystem interface
  • 4640544 New I/O: Complete socket-channel functionality
  • 4607272 New I/O: Support asynchronous I/O

让人期待太久太久了,终于来了,Java在大规模并发、高性能方面又进一步,JSR 203应该会在JDK 7中实现,届时随着JDK 7的发布,将会有更多的基础软件使用Java实现,而且有极好的性能。

在磁盘I/O和网络大规模并发I/O方面都会得到更好的性能。

可以预见受益的程序:
1、WEB服务器 Tomcat、Jetty等,在Windows下,Java将可以使用IOCP,而不是现在nio所用的select,网络并发性能将会得到大幅度提升。在Linux下则应该改变不多,毕竟linux现在并发最好性能的网络I/O EPOLL,JDK 6.0 nio的缺省实现就是epoll。
2、数据库应用程序。如Derby、Berkeley DB Java Edition等使用Java实现的数据库,性能将会得到更好的提升,有望能够诞生和Oracle、SQL Server一样强大的100% Pure Java的数据库系统。
3、其他网络应用程序。例如DNS、LDAP等,随着MINA之类的框架更强大和JDK的原生支持,将会越来越多的服务器程序使用Java实现。

posted @ 2008-05-09 02:54 温少的日志 阅读(1741) | 评论 (3)编辑 收藏
 

在新项目中,除了一些框架所依赖的配置文件使用XML外,基本没有使用XML。JSON基本替代了原来XML在程序内的位置。在以前,我们不愿意使用一种私有的格式,于是选择了XML。选择XML的理由,可能是大家都用它,所以我们也用它。

XML 是一种很好的技术,但是目前的情况来看,XML被滥用了,SOAP是XML被滥用的一种典型,程序内部的表示使用XML也是滥用的一种典型。看到的一种情况,一个对象toString使用XML格式输出,导致日志文件十分罗嗦,调试时,在watch窗口中看到一大堆<tag>。

在新项目中,认真考虑这种情况,找到了另外一种选择,那就是JSON。选择JSON的理由很充分:
1、JSON的解释性能要比XML要好,要简洁紧凑。
2、可读性要比XML好。JSON本身就是JavaScript的语法,和程序员的思维,而非文档编写的思维。
3、JavaScript原生支持,客户端浏览器不需要为此使用额外的解释器,在web环境中使用特别合适。

在java中使用json,目前需要注意一些情况:
1、目前开源的JSON-LIB代码质量不好,最好是在此基础之上修改一个版本,或者自己重新开发一个版本。
2、使用new Date的方式替代JSON-LIB中的{year:2007, month:12, ....}之类的方式
3、JSON-LIB中,object的propertyName在输出的时候,都带上"",例如{"name": "温少"}, 其中name是的双引号是不必要的,在输出时应该判断,不需要的就是就不加上"",减少网络流量。
4、JSON的解释器中,应该支持简单的表达式,例如new Date()、new Date(2939234723)之类的,这使得JSON的表达能力会更强一些。
5、JSON应该分两种,一种只支持简单格式,类似开源的JSON-LIB,一种是通过JavaScript解释器来实现的。后者在程序中传输数据时,能够得到更强大的表达能力,但是也会导致安全问题,需要慎重使用。
posted @ 2008-03-08 14:24 温少的日志 阅读(3662) | 评论 (12)编辑 收藏
 

1、 XP中的结对编程。XP编程中,有一些思想总结的很好,例如测试驱动,但又有极度的荒唐的就是结对编程。结对编程是我看到过的最荒唐最可笑的软件工程方 法,两倍的投入,一半的产出,可谓事倍功半。以前看结对编程只是觉得荒唐可笑,后来看了李安的电影《断背山》,觉得以“断背”来形容结对编程最适合了,结 对编程简直就是专门为“男同志”们度身定做的软件工程方法,你想一对“男同志”,每天手牵手背靠背进行“结对编程”,是多么“浪漫有趣”的事情。不过这只 对“男同志”们的浪漫有趣,对工作本身一点也不有趣!
--------------

2、JDO投票闹剧(2004-2005)。 一个通过黑客式静态AOP方式旁门左道实现的持久化技术JDO,竟然会被一些人追捧,这本身就是一个很荒唐的事情了。在JCP的投票中,JDO被否决了, 这一点也不奇怪,奇怪的是投票结果出来之后的闹剧。一些人以“政治阴谋论”来说事,说JDO不被通过,是因为政治原因,而非技术原因,这个荒唐的理由竟然 被社区的很多人相信了,一片声讨,JCP迫于压力,重新投票,通过了JDO相关的JSR。但是JDO并没有因此有一点点起色,一直沉沦至今。JDO通过静 态AOP(enhance)的方式使得代码无法调试,就单这一点,就足以使JDO永远无法流行。

这件事情很明确表明两点:1)、不要相信一些技术作家的判断力;2)、普通的大众没有判断能力,会人云亦云。

当年荒唐的文章选录:
《程序员》2005年第2期 http://blog.csdn.net/gigix/archive/2005/01/21/262163.aspx
---------------
posted @ 2008-02-09 15:39 温少的日志 阅读(976) | 评论 (5)编辑 收藏
 

竟 然64个annotation,没有分类,放在同一个package下,同一个package(javax.persistance)还有其他java文 件,共有88个java文件。不看内容本身,单从表面,都觉得这是混乱不堪的事情。这是那个猪头的杰作?glassfish上下载的源码中,这些java 文件似乎都没有author,估计也不好意思把名字放出来见人吧!

------

觉得对象关系存储方面一直没有突破,也没有好的产品出来,其中一个原因,就是从没有过优秀的工程师投身过这个领域。关系数据库为什么能够一直坚守领地,成为绝大多数商业应用的基石,其中一个原因就是有过大量的精英投身于此,包括两个图灵奖获得者。

关 系数据库,为了描述关系,创造一门SQL语言,将关系一些操作,例如投影(select)、选择(where)、分组(group by)等等,抽象得形象易懂,功能强大。对于数据的操作,SQL语言是最强大,也是最方便的,也是最易于使用的。一些非程序员的IT从业人员,非计算机专 业的人员都能够熟练掌握SQL。

OO和Relational都是伟大的技术,从计算机最高荣誉奖可以看出这两个技术的伟大。OO的图灵奖获得者是三个,Relational的图灵奖获得者是两个。

面向对象技术自1967年simula引进以来,所想披靡,93年-98年从C++开始流行,然后到Java,成为主流编程技术。Relational没有OO那么辉煌,但是在数据存储方面的地位固如磐石,长期占据绝对的地位。

曾 经OO技术涉足于数据存储领域,但终究没有成功。面向对象数据库的变现总是差强人意,面向对象的方式操作数据,总是不如使用关系那么方便,那么灵活,那么 易于使用,那么好的性能。于是人们在数据存储和处理方面,不在青睐面向对象技术,而是仍然使用关系方式,使用SQL语言,使用关系运算操作数据。面向对象 数据库成了昙花一现的东西,并且可能永远都不会再流行了。

OO成了主流编程技术,Relational占据了绝对的数据存储地位,这两大技术需要交互,需要桥接,这需要OR-Mapping。Relational虽然好,但我们也要与时俱进,所以也需要OR-Mapping。

但 是,做OR-Mapping时,不积极吸取relational方式对数据处理的灵活性、方便性、简单性,而只强调Relational和对象之间的的 Mapping,试图以面向对象的方式操作数据,这是错误的方向。以前的EJB、现在Hibernate、JPA都犯了同样的错误,试图以更面向对象的方 式操作数据,从而导致复杂混乱的模型,这也是JPA的现状吧。例如user.getGroup(),目前的ORM试图以纯OO的方式操作数据,所引起的 LazyLoad、n+1等问题,使得事情变得复杂而且混乱不堪。

一些开发人员,去学习Hibernate,不学习SQL,有人提倡,只需要了解面向对象编程技术,不需要了解关系技术,亦属于本末倒置。需求人员都会用的SQL语言,对数据操作最方便最简单最强大的SQL语言,竟然成了令人生畏的纸老虎,可笑啊。

-------------

以下是过去的一些业界浮躁不理智:

1、面向对象数据库。曾被热衷而吹捧,面向对象数据库的变现总是差强人意,面向对象的方式操作数据,总是不如使用关系那么方便,那么灵活,那么易于使用,那么好的性能。于是人们在数据存储和处 理方面,不在青睐面向对象技术,而是仍然使用关系方式,使用SQL语言,使用关系运算操作数据。面向对象数据库成了昙花一现的东西,并且可能永远都不会再 流行了。

2、 JDO投票闹剧。2004-2005年,JDO的JSR在JCP投票被否决的,无聊者在Java社区以及媒体发起闹事,阴谋论其为政治谋杀,几大公司是的 迫于形象,重新投票使得JDO被通过,但JDO这种静态AOP叫雕虫小计式技术,不单开发过程不方便,而且会使得"enhance"之后的代码不可调试。 这完全是对开发者不友好的技术,没有前途的技术,竟然会有人为它在JCP投票不通过鸣不平。这件事情使得我更坚信一点,不要相信那些技术编辑的判断力。

3、 AOP。也是最近这几年流行的一个名词了。起了一个和OOP相似的名字,但是和伟大的OOP相比,它完全不算是什么。AOP只是一种很小很小的技巧而已, 静态的AOP是黑客式的插入代码,会导致代码不可调试,动态的AOP能力有限,AOP最常被引用例子“日志AOP”是不合适,有用的日志通常是精心设计 的,AOP方式的日志在生产环境中基本上是不可用。OO这么多年,这么为伟大,人们总是希望自己能做点什么和伟大的OO相比,于是命名为AOP,这是一个 可笑的名字,前些年还有人谈论面向对象的未来是面向事实,也是同样的可笑。AOP有价值,但它是一种小技巧,和名字不般配。

--------------

目前在流行,但是可能是不理智的技术:

1、hibernate之类的ORM,试图以面向对象方式操作数据,和面向对象数据库一样,重蹈覆辙。
2、Ruby,一个小脚本语言,只是因为动态类型、mixin之类的功能,还没有被证明有生产力,有效益可用的脚本语言,就被媒体吹到天上去。Ruby有价值,但是最终结果会离大家的期待相差甚远。
posted @ 2008-02-02 02:56 温少的日志 阅读(5233) | 评论 (19)编辑 收藏
 



中国最大的在线记账及商务管理平台—金蝶“友商网”(
www.youshang.com)正式上线!请立刻体验!
posted @ 2007-11-29 15:42 温少的日志 阅读(1461) | 评论 (13)编辑 收藏
 
     摘要: 本文描述一种ID生成算法  阅读全文
posted @ 2007-11-16 07:09 温少的日志 阅读(2706) | 评论 (8)编辑 收藏
 
     摘要: 本文介绍流行的非阻塞算法关键思想Compare And Set在数据库开发中的应用  阅读全文
posted @ 2007-11-13 06:33 温少的日志 阅读(1443) | 评论 (4)编辑 收藏
 
由于在实际工作中使用到了mina,所以一直关注其mail-list。

最近mina的mail-list讨论的一个问题,就是提供的manual close connector,这个问题可害惨我了。

原来的Connector,无论是SocketConnector或者VmPipeConnector,都是没有提供close方法的,而且不会自动释放。

原来做得一个网络程序客户端,每次重新创建的时候,都会new SocketConnector,可是,SocketConnector不会被GC回收的,所使用的线程和内存都不会自动释放,这个程序在服务器断开时会重连,于是,当服务器重启或者网络中断时,内存泄漏就产生了,程序慢慢的占用更多的内存,直至崩溃!

解决此问题的办法就是,要么使用Singleton,要么使用即将发布的1.1.3!


温少 2007-09-29 21:08 发表评论
posted @ 2007-09-29 21:12 温少的日志 阅读(1189) | 评论 (0)编辑 收藏
 
使用maven2一段时间了,我基本把我自己能够迁移的project都转换为maven2 project,以下是我的一点感想。

(原作者温少,转载请注明)

乱世盼英雄


现在的软件开发,比过去的软件开发要复杂得多。包括在参与开发的人数、代码规模、复杂的需求、依赖包的复杂性、使用到更多的技术、多个项目之间的复杂依赖关系。

现在的开发人员,要掌握的技术要比过去的开发人员要多,不是现在的开发人员比以前的开发人员本身更优秀,而是拥有更多的资料、更多的学习机会、更多更大规模的时间,同时软件行业也在发展。说一句题外话,老程序员,如果不与时俱进,靠老本,是无法和新一代程序员竞争的,当然,老程序员,拥有更多的经验,掌握新技术的速度更快,这又是另外一回事。

开发人员掌握的技术更复杂了,项目用得的技术自然也是更复杂,例如一个web项目,可能使用到很多技术,面向对象、泛型、or-mapping、依赖注入(spring-framework)、全文检索(lucene)、数据库、集群、工作流、web service等等。

由于使用了多种技术,这些技术可能是JDK提供的,也可能是第三方开源组织提供的,或者不同的商业公司提供的。

于是出现了一个新的难题,就是包依赖复杂性。以前,你很难想象你的代码依赖数十个不同开源组织、商业公司提供的库。例如,我们经常使用的log4j、junit、easymock、ibatis、springframework,每个组件都有悠久的历史,存在不同的版本,他们之间版本还有依赖关系。

项目依赖的复杂性,经常的,一个较大部门有10-30个项目是常事,项目之间有不同版本的依赖关系,部门与部门之间的项目也存在复杂的版本依赖关系。

Eclipse本身提供Project的依赖,但是简单的依赖显然解决不了问题。例如Project B依赖Project A,Project A依赖第三方的jar包abc-1.0.jar,那么需要在两个项目的lib下都存放abc-1.0.jar,这产生冗余,当Project数量多起来,这个冗余就产生了管理问题,如果需要将abc-1.0.jar升级为abc-1.1.jar,则需要在两个Project中同时修改,如果Project数量达到10个以上,而且是不同项目组维护的项目,这个就是非常麻烦的事情。而且Project A修改依赖,为啥需要Project B也作相应的修改呢?

需要解决此问题,就需要在Project A的包中描述其依赖库的信息,例如在META-INFO记录所以来的abc-1.0.jar等。Eclipse的plug-in拥有类似的方案,但是这样一来,就使得开发Project B的项目组,需要把Project A的代码从源代码库中check out出来。在依赖链末端的项目组是很惨的。

由于Project数量众多,关系复杂,dailybuild的ant脚本编写成了很麻烦的事情,使用Project依赖的方式,更加使得编写dailybuild ant script是非常痛苦的事情。

当然也可以不使用project依赖的方式,而使用artifact lib的依赖方式,但是artifact lib的依赖方式,就是同时修改多个project,互相调试时带来了痛苦。

在以前,我们面临这些问题时,唯一的感觉就是,这事情TMD的太麻烦,几乎是失控了。

maven的出现,解决这种问题看到了希望。maven出现的原因就是,现在的开发管理复杂度达到了一定程序,需要专门的开发管理工具,这样的工具需要涵盖开发的各个阶段,包括工程建立、配置依赖管理、编译、测试、产生分析报告、部署、产生制品等阶段。目前,各个阶段的工具都存在,但是不是集成的,对使用带来了很大麻烦。maven集成了这些工具,提高了统一的环境,使得使用更简单。

现在maven非常流行了,apache上所有java project都已经build by maven,其他跟进的开源项目非常多,例如mule、hibernat等等,商业公司也很多在采用,sun公司提供有maven2 repository。

现在,2007年,如果你还没采用maven project,你可能需要思考一下,是否你使用了不恰当的方式管理的代码,或者你落伍了?

maven的一些常用任务

compile 编译代码
test 运行单元测试
package 打包代码
site 产生报告,例如java doc、junit的通过率报告和覆盖率报告、findbugs的分析报告等等。
assembly 使用需求产生assembly,例如把生成一个程序目录,包括bin、config、lib、logs,把依赖包放在lib中。
deploy 部署制品到repository中。

这些都是常用的任务,在以前编写脚本很麻烦,现在在maven中,一切都是很简单,需要仔细设置时功能又强大到令人惊叹,例如site的fml,assembly。

maven资源库

maven官方提供了一个常用lib的资源库,包括apache的所有java项目,开源常用的基本都能够找到,例如mule、c3p0、easymock、hibernate、springframework、json等等,版本齐全,任你挑选。

可以部署本地资源库代理提高下载速度。使用maven proxy。

maven体系结构


maven使用plug-in的体系,使用很好的自动更新策略,本身用到的jar都是lazy download的,可以指定download的repository。这样,初始下载的maven是一个很小的程序,使用的时候从本地的资源库或者本地代理资源库高速下载lib。maven的插件体系,充分利用了internet的资源丰富和局域网的高速带宽,使用本地repository时,可以享受到每秒钟数M的下载速度,感觉就是,人生真是美妙!

elcipse的plug-in体系,就不是那么好用了,我们使用eclipse的find and install功能下载或者更新插件时,慢如蜗牛,依赖缺失时的烦恼,更新一个plug-in,可能耗费你数个小时,第三方的plug-in的下载服务器可能更慢,例如subversive的plugin-in,有一次我花了两天还没下载好,只好使用下载工具下载好,copy到plug-in目录下。此时,我们总是感叹,人生总是有很多烦恼事啊!

相比之下,高下立判!在此我不是说eclipse的plug-in体系结构设计不好,eclipse的插件体系非常优秀,但是还有很大改进空间!




温少 2007-09-24 08:34 发表评论
posted @ 2007-09-24 08:34 温少的日志 阅读(2915) | 评论 (7)编辑 收藏
 
在beep4j上作了一些修改,并且在此之上实现了一个基于BEEP协议的服务器框架。

BEEP协议提供了Session、Channel、Greeting、Profile、Frame等概念,这些基础概念之上,很容易进行更高级的应用层协议设计。

BEEP协议的特征和能力


长连接
对等通讯
请求\应答式交互
在一个Session中创建多个独立的传输通道(Channel)
在一个通道中进行多个异步请求(滑动窗口)
可以使用不同的消息编码方式,包括二进制、文本、XML等,类似SMTP的MIME,使得可以在高效的二进制、易懂的文本之间作出选择。

这是一个在传输层和应用层之间的协议,应用场景很广泛,RFC标准化,官方网站为http://www.beepcore.org/。很多公司都有相应的支持,包括IBM。在不同语言下都是相应的实现,包括C、Java、Python、Ruby、JavaScript Beep client等等。

关于ContentType和Codec


在Java程序之间通讯,前期可能不希望作更多的协议编码、解码工作,使用spring bean xml格式传输是一种方式。

在一些对效率不是特别高,又不喜欢使用机器友好的XML的场景,可以使用JSON的编码方式。

在一些对效率要求很高的场景,ASN.1或者自定义的二进制编码格式。

甚至使用土土的序列化编码方式





温少 2007-09-22 08:07 发表评论
posted @ 2007-09-22 08:07 温少的日志 阅读(898) | 评论 (0)编辑 收藏
 

匆忙写成,以后会慢慢补充

请用力一击

中等规模的并发程序设计
http://www.cnblogs.com/Files/jobs/2007-5-9-concurrent-ppt.rar

2007-5-10修改版(带参考文档)
http://www.cnblogs.com/Files/jobs/2007-5-10-concurrent-ppt.rar

posted @ 2007-05-08 08:15 温少的日志 阅读(564) | 评论 (1)编辑 收藏
 
2007年4月刊《程序员》,专题为“多核时下的软件开发”。《程序员》并非阳春白雪,它面向大众程序员。面向大众的《程序员》介绍多核、并发,也意味着并发程序设计的开始进入中国大众程序员的视野。

并发程序设计,在很多的书籍或者文章中,都会提到他的一个特点,复杂。这个特性,也导致了在以往并发程序设计只为高级程序员所专用。

复杂度并非事物的固有属性,并发程序设计的复杂,是我们主观认为。我们认为并发程序设计复杂,是因为我们还没有掌握一些使其简单化、清晰化的方法。当我们掌握相关方法,研究彻底,并发就会变得简单。这个过程已经开始了。

以 往,我们需要直接使用一些低级并发概念来构造系统,不断发明轮子,容易出错,难以调试,这种的并发程序设计当然复杂,因此也只能为高级程序员所专用。如此 环境,就如同Dijkstra给我们带来结构化程序设计之前的世界一般。很幸运的是,一些软件业的先驱们,已经抽象出一些概念,能够使得并发程序设计简单 化,清晰化。例如Future、Lock-free思想等。

在主流编程语言中,Java走在最前头,理念领先,提供了实用的库。在 Java SE 5.0中就提供了util.concurent包,包括了Future、Executor、BlockingQueue等,一系列lock-free的数 据结构,例如ConcurrentMap。包括并发流程控制工具类:CountDownLatch、CycliBarrier。还有精巧好用的 DelayQueue(参考我之前写过的文章http: //www.cnblogs.com/jobs/archive/2007/04/27/730255.html)。使用这些概念以及提供的模式,能够使 得编写并发程序简单化。

C++中,Herb Sutter在Visual C++中加入了很多支持并发的语法特性,包括atomic、future等。boost的线程库开始引入了第一个高级概念barrier。

Windows 平台本身提供了功能强大的并发API,包括WaitForSingle系列,WaitForMulti系列,Auto和Manual模式的Event等 等。.NET平台基本没有任何自有的并发库和工具类,完全是Windows API的简单封装。可以这么说,.NET的类库没有为并发作任何事情,完全吃Windows API的老本。

如同Herb Sutter认为,我们很幸运处于并经历这个软件大变革(并发)。并发进入主流这个过程将会延续数年,Herb Sutter认为是2007-2012。
参考我以前写的一篇文章(Herb Sutter的一些观点 http://www.cnblogs.com/jobs/archive/2006/11/12/558078.html)

类 似的场景也有,早期面向对象技术,也只为少数高级程序员所掌握,现在刚入门的程序员都能说上一大通。数据结构算法也是,早期只为少数优秀程序员所掌握,但 现在主流的开发环境中就包括了主要的数据结构和算法,会用的人一把一把,会用List、Hashtable、快速排序一点也不酷。并发程序设计也一样,将 不再是阳春白雪!

面向对象技术在最初在Simula语言中引进,顾名思义,最初朴素的面向对象思想就是模拟,在程序中模拟真实世界。这种 “模拟”,使得程序的组织清晰化,简单化。但真实世界是充满着并发。真实世界的并发要比虚拟环境中的并发要复杂的多,但是人们轻松应付,由此,我们有足够 的理由相信,并发程序设计将不会是一种复杂难掌握的技术。
posted @ 2007-05-01 08:54 温少的日志 阅读(1398) | 评论 (2)编辑 收藏
 

我们谈一下实际的场景吧。我们在开发中,有如下场景

a) 关闭空闲连接。服务器中,有很多客户端的连接,空闲一段时间之后需要关闭之。
b) 缓存。缓存中的对象,超过了空闲时间,需要从缓存中移出。
c) 任务超时处理。在网络协议滑动窗口请求应答式交互时,处理超时未响应的请求。

一种笨笨的办法就是,使用一个后台线程,遍历所有对象,挨个检查。这种笨笨的办法简单好用,但是对象数量过多时,可能存在性能问题,检查间隔时间不好设置,间隔时间过大,影响精确度,多小则存在效率问题。而且做不到按超时的时间顺序处理。

这场景,使用DelayQueue最适合了。

DelayQueue 是java.util.concurrent中提供的一个很有意思的类。很巧妙,非常棒!但是java doc和Java SE 5.0的source中都没有提供Sample。我最初在阅读ScheduledThreadPoolExecutor源码时,发现DelayQueue 的妙用。随后在实际工作中,应用在session超时管理,网络应答通讯协议的请求超时处理。

本文将会对DelayQueue做一个介绍,然后列举应用场景。并且提供一个Delayed接口的实现和Sample代码。

DelayQueue是一个BlockingQueue,其特化的参数是Delayed。(不了解BlockingQueue的同学,先去了解BlockingQueue再看本文)
Delayed扩展了Comparable接口,比较的基准为延时的时间值,Delayed接口的实现类getDelay的返回值应为固定值(final)。DelayQueue内部是使用PriorityQueue实现的。

DelayQueue = BlockingQueue + PriorityQueue + Delayed

DelayQueue的关键元素BlockingQueue、PriorityQueue、Delayed。可以这么说,DelayQueue是一个使用优先队列(PriorityQueue)实现的BlockingQueue,优先队列的比较基准值是时间。

他们的基本定义如下
public interface Comparable<T> {
    
public int compareTo(T o);
}

public interface Delayed extends Comparable<Delayed> {
    
long getDelay(TimeUnit unit);
}

public class DelayQueue<extends Delayed> implements BlockingQueue<E> { 
    
private final PriorityQueue<E> q = new PriorityQueue<E>();
}

DelayQueue内部的实现使用了一个优先队列。当调用DelayQueue的offer方法时,把Delayed对象加入到优先队列q中。如下:
public boolean offer(E e) {
    
final ReentrantLock lock = this.lock;
    lock.lock();
    
try {
        E first 
= q.peek();
        q.offer(e);
        
if (first == null || e.compareTo(first) < 0)
            available.signalAll();
        
return true;
    } 
finally {
        lock.unlock();
    }
}

DelayQueue的take方法,把优先队列q的first拿出来(peek),如果没有达到延时阀值,则进行await处理。如下:
public E take() throws InterruptedException {
    
final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    
try {
        
for (;;) {
            E first 
= q.peek();
            
if (first == null) {
                available.await();
            } 
else {
                
long delay =  first.getDelay(TimeUnit.NANOSECONDS);
                
if (delay > 0) {
                    
long tl = available.awaitNanos(delay);
                } 
else {
                    E x 
= q.poll();
                    
assert x != null;
                    
if (q.size() != 0)
                        available.signalAll(); 
// wake up other takers
                    return x;

                }
            }
        }
    } 
finally {
        lock.unlock();
    }
}

-------------------

以下是Sample,是一个缓存的简单实现。共包括三个类Pair、DelayItem、Cache。如下:

public class Pair<K, V> {
    
public K first;

    
public V second;
    
    
public Pair() {}
    
    
public Pair(K first, V second) {
        
this.first = first;
        
this.second = second;
    }
}

--------------
以下是Delayed的实现
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

public class DelayItem<T> implements Delayed {
    
/** Base of nanosecond timings, to avoid wrapping */
    
private static final long NANO_ORIGIN = System.nanoTime();

    
/**
     * Returns nanosecond time offset by origin
     
*/
    
final static long now() {
        
return System.nanoTime() - NANO_ORIGIN;
    }

    
/**
     * Sequence number to break scheduling ties, and in turn to guarantee FIFO order among tied
     * entries.
     
*/
    
private static final AtomicLong sequencer = new AtomicLong(0);

    
/** Sequence number to break ties FIFO */
    
private final long sequenceNumber;

    
/** The time the task is enabled to execute in nanoTime units */
    
private final long time;

    
private final T item;

    
public DelayItem(T submit, long timeout) {
        
this.time = now() + timeout;
        
this.item = submit;
        
this.sequenceNumber = sequencer.getAndIncrement();
    }

    
public T getItem() {
        
return this.item;
    }

    
public long getDelay(TimeUnit unit) {
        
long d = unit.convert(time - now(), TimeUnit.NANOSECONDS);
        
return d;
    }

    
public int compareTo(Delayed other) {
        
if (other == this// compare zero ONLY if same object
            return 0;
        
if (other instanceof DelayItem) {
            DelayItem x 
= (DelayItem) other;
            
long diff = time - x.time;
            
if (diff < 0)
                
return -1;
            
else if (diff > 0)
                
return 1;
            
else if (sequenceNumber < x.sequenceNumber)
                
return -1;
            
else
                
return 1;
        }
        
long d = (getDelay(TimeUnit.NANOSECONDS) - other.getDelay(TimeUnit.NANOSECONDS));
        
return (d == 0? 0 : ((d < 0? -1 : 1);
    }
}



以下是Cache的实现,包括了put和get方法,还包括了可执行的main函数。
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Cache<K, V> {
    
private static final Logger LOG = Logger.getLogger(Cache.class.getName());

    
private ConcurrentMap<K, V> cacheObjMap = new ConcurrentHashMap<K, V>();

    
private DelayQueue<DelayItem<Pair<K, V>>> q = new DelayQueue<DelayItem<Pair<K, V>>>();

    
private Thread daemonThread;

    
public Cache() {

        Runnable daemonTask 
= new Runnable() {
            
public void run() {
                daemonCheck();
            }
        };

        daemonThread 
= new Thread(daemonTask);
        daemonThread.setDaemon(
true);
        daemonThread.setName(
"Cache Daemon");
        daemonThread.start();
    }

    
private void daemonCheck() {

        
if (LOG.isLoggable(Level.INFO))
            LOG.info(
"cache service started.");

        
for (;;) {
            
try {
                DelayItem
<Pair<K, V>> delayItem = q.take();
                
if (delayItem != null) {
                    
// 超时对象处理
                    Pair<K, V> pair = delayItem.getItem();
                    cacheObjMap.remove(pair.first, pair.second); 
// compare and remove
                }
            } 
catch (InterruptedException e) {
                
if (LOG.isLoggable(Level.SEVERE))
                    LOG.log(Level.SEVERE, e.getMessage(), e);
                
break;
            }
        }

        
if (LOG.isLoggable(Level.INFO))
            LOG.info(
"cache service stopped.");
    }

    
// 添加缓存对象
    public void put(K key, V value, long time, TimeUnit unit) {
        V oldValue 
= cacheObjMap.put(key, value);
        
if (oldValue != null)
            q.remove(key);

        
long nanoTime = TimeUnit.NANOSECONDS.convert(time, unit);
        q.put(
new DelayItem<Pair<K, V>>(new Pair<K, V>(key, value), nanoTime));
    }

    
public V get(K key) {
        
return cacheObjMap.get(key);
    }

    
// 测试入口函数
    public static void main(String[] args) throws Exception {
        Cache
<Integer, String> cache = new Cache<Integer, String>();
        cache.put(
1"aaaa"3, TimeUnit.SECONDS);

        Thread.sleep(
1000 * 2);
        {
            String str 
= cache.get(1);
            System.out.println(str);
        }

        Thread.sleep(
1000 * 2);
        {
            String str 
= cache.get(1);
            System.out.println(str);
        }
    }
}

运行Sample,main函数执行的结果是输出两行,第一行为aaa,第二行为null。
posted @ 2007-04-27 20:04 温少的日志 阅读(2011) | 评论 (2)编辑 收藏