新写了一个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
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版本已经具备了基本功能,可以部署简单应用了,但是由于其功能不够完备,目前大多数应用要部署在其上,都会要做相当大的修改或者无法实现。
我在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
最近花了较多时间学习单点登陆以及相关的安全技术,做一个简单的总结,发表我的一些看法。抛砖引玉,希望各位朋友参与讨论。
单点登陆,鸟语原文为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技术 推动实时网络发展
网站导航:
博客园首页 个人主页 新闻 社区 博问 闪存 找找看
现在很多开源项目在使用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
Eclipse包含很多插件,插件之间有复杂的依赖关系,如果使用单独下载安装的方式,容易遗失部分需要依赖的插件。
在Ecliipse的Software Update功能中安装插件,能够解决插件依赖的问题,但是在Eclipse 3.4之前的版本,Software Update不能够多线程同时下载,遇到网速较慢的更新站点时,需要漫长的等待,有时候安装一个插件,需要数个小时,甚至更久。
在Eclipse 3.4之后,Software Update有了很大的改变,可以多线程下载了,但是不能手工选择镜像,它会笨笨的选择一些很慢的站点,效果变得更差了,下载速度时快时慢,但是经常都比以前手工选择镜像要慢。经常选择一些只有数百B速度的下载站点,令人抓狂!
所以说,Eclipse 3.4的Software Update功能依然令失望。
期待数年,终于盼来了新版的Software Update功能,但是新版的更差了,哎。。。
摘要: 我们在开发中,经常需要遍历一个目录下的所有文件,常用的办法就是使用一个函数递归遍历是常用的办法。但是递归函数的缺点就是扩展不方便,当然你对这个函数加入一个参数FileHandler,这样扩展性稍好一些,但是仍然不够好,比如说,不能根据遍历的需要中途停止遍历,加入Filter等等。我实现了一个FileIterator,使得遍历一个目录下的文件如何遍历一个集合中的元素一般操作。
阅读全文
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实现。
在新项目中,除了一些框架所依赖的配置文件使用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解释器来实现的。后者在程序中传输数据时,能够得到更强大的表达能力,但是也会导致安全问题,需要慎重使用。
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
---------------
竟
然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有价值,但是最终结果会离大家的期待相差甚远。
中国最大的在线记账及商务管理平台—金蝶“友商网”(www.youshang.com)正式上线!请立刻体验!
摘要: 本文描述一种ID生成算法
阅读全文
摘要: 本文介绍流行的非阻塞算法关键思想Compare And Set在数据库开发中的应用
阅读全文
由于在实际工作中使用到了mina,所以一直关注其mail-list。
最近mina的mail-list讨论的一个问题,就是提供的manual close connector,这个问题可害惨我了。
原来的Connector,无论是SocketConnector或者VmPipeConnector,都是没有提供close方法的,而且不会自动释放。
原来做得一个网络程序客户端,每次重新创建的时候,都会new SocketConnector,可是,SocketConnector不会被GC回收的,所使用的线程和内存都不会自动释放,这个程序在服务器断开时会重连,于是,当服务器重启或者网络中断时,内存泄漏就产生了,程序慢慢的占用更多的内存,直至崩溃!
解决此问题的办法就是,要么使用Singleton,要么使用即将发布的1.1.3!
使用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的插件体系非常优秀,但是还有很大改进空间!
在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或者自定义的二进制编码格式。
甚至使用土土的序列化编码方式
匆忙写成,以后会慢慢补充
请用力一击中等规模的并发程序设计
http://www.cnblogs.com/Files/jobs/2007-5-9-concurrent-ppt.rar2007-5-10修改版(带参考文档)
http://www.cnblogs.com/Files/jobs/2007-5-10-concurrent-ppt.rar
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语言中引进,顾名思义,最初朴素的面向对象思想就是模拟,在程序中模拟真实世界。这种
“模拟”,使得程序的组织清晰化,简单化。但真实世界是充满着并发。真实世界的并发要比虚拟环境中的并发要复杂的多,但是人们轻松应付,由此,我们有足够
的理由相信,并发程序设计将不会是一种复杂难掌握的技术。
我们谈一下实际的场景吧。我们在开发中,有如下场景
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<E 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。
china-pub购书网址:http://www.china-pub.com/computers/common/info.asp?id=34017
1、总体感受
a) 这本书主要介绍的是intel平台下的多核程序设计技术,Windows介绍较多,Linux介绍较少,Java更少。作者是Intel公司的平台架构师,我们知道wintel联盟,书中的内容如此分布也是正常。
b) 此书让我懂得了很多硬件方面的并行知识。
c)
此书介绍Windows API中和并发相关的部分,很详尽,比Jeffrey
Richter的《Windows核心编成》有深度多了,也精辟多了。显然《多核程序设计》作者属于有经验的工程师,Jeffrey
Richter只是一个写手,两者没有可比性。不过这方面的知识偶早已涉猎,当作复习一遍罢了。
d) 此书偏向底层,硬件和操作系统层面。更高层面的技术介绍较少。
e) 第一次了解OpenMP技术的一些细节。以前只听说,也没查过任何相关资料,在此书中看到了相关部分,挺有意思的,感觉那些语法很有趣。Herb Sutter也是要在语法方面动手。反正我在有了一个粗浅认识之后,觉得很有意思。
-------------------------
2、并发流程控制
Fence
在Java
中对应的是java.util.concurrent.CountDownLatch。最初接触CountDownLatch的时候,由于其实现很简单,
当时觉得是一个可有可无的工具类。但后来在不同的场景多次使用,发现很有用。在此书中再次发现类似的Fence,用于在共享存储多处理器或者多核环境中,
确保存储操作的一致性。我猜这属于业界并发流控制的典型手段了。
Barrier
在Java中对应的是java.util.concurrent.CyclicBarrier。在应用程序中,一个场景就是和定时器结合使用,countDown、await、reset,做定时定量批量处理。
我猜这也属于业界并发流程控制的典型手段了。
(CountDownLatch和CycliBarrier的实现代码都很简单,但很有用,他们都属于并发流程控制的典型手段)
-------------------------
3、非阻塞算法
InterLocked在Java中对应的是java.util.concurrent.atomic.xxx
书中提到了cache行乒乓球现象导致的性能问题,提高了非阻塞算法的复杂性问题。
关于性能问题,developerworks上有一片文章,有测试数据:
《Java 理论与实践: 流行的原子》 (http://www.ibm.com/developerworks/cn/java/j-jtp11234/index.html)
文章中的测试数据表明,直接使用atomic在1个和2个处理器时是最好的,4个处理器以上,使用java.util.concurrent.locks.ReentrantLock性能更好。
java.util.concurrent包,提供了很多高级的概念,隐藏了非阻塞算法带来的复杂度,其底层框架达到了最佳性能。
-------------------------
4、任务分解、数据分解以及数据流分解
此书中明确提出了这三个概念,很有用,让我在这方面的知识概念清晰化了。
任务分解
Java
中的Executor,提供了任务分解的手段和模式。任务分解提交给Executor执行。java.util.concurrent中提供了
Future,用于任务提交者和Executor之间的协调。Future是一种很好的手段,在很多涉及并发的库都提供。例如C++网络并发库中提供了
Future,Herb Sutter要在Visual C++中引入Future。
数据分解
数据分解的手段很多也很常见。
Java中,提供了一种高级的数据分解协同模式java.util.concurrent.Exchanger这个类。早在Java SE
5.0时,Exchanger只支持2Parties,Java SE 6.0支持n
parties。偶想象过一些很酷的应用场景,写过模拟测试,但一直没有机会用于实际开发中。
数据流分解
书中提到了众多周知的producer/consumer问题。
其实java.util.concurrent.Exchanger类,既有数据分解,又有数据流分解,exchanger中的producer和consumer的角色会互换的,很有意思。
-------------------------
5、作为Java程序员的思考
Java SE 5.0之后,提供了util.concurrent包,功能齐全,性能卓越,非常优秀。从此书来看,业界流行的流程控制手段和并发程序设计方法一个不落。我们应该感谢伟大的Doug Lea,他为我们带了一个如此完美的并发库!
一旦方案想清楚,剩余部分的工作效率瓶颈就在于你的手速了。最近一直看起点中文网上的《师士传说》,主角叶重一个强项就是手速。
最基本的就是盲打。不会盲打的通常属于“编码低能儿”。身边也有不会盲打的朋友,他们通常都有一个问题,就是眼高手低,说说还行,动手就不行。当然他们能够在IT研发领域还混得很好,是因为在其他方面拥有优秀的能力。
熟练掌握快捷键是关键。键盘和鼠标之间通常有较大的距离,手经常在键盘和鼠标之间移动,会降低效率,也容易导致疲劳,用鼠标过多,也容易导致龌龊的鼠标手。解决这个问题的办法,就是纯键盘操作,其实很多IDE的快捷键功能强大,足以让你纯键盘操作,高效率编码。
我比较熟悉的IDE是Eclipse,就以Eclispse来说吧。
Eclipse的keys列表中,属于Eclipse本身有180多个快捷键,要提高编码速度,就应该熟练使用其中绝大多数。
练习的办法:
1、在Windows/Preferences/General/keys中,使用Export,把快捷键导出,导出的格式是csv格式,Windows下可以用Excel直接打开,Linux下可以用OpenOffice打开,打开时选择分隔符为“,”。
2、挨个练习使用。每天练习一部分,反复练习,坚持一段时间。
3、开始的时候,把鼠标放到一个不方便使用的角落,尽量不要让自己用鼠标。
4、快捷键的组合使用需要加强训练。在不同场景下,认真考虑用怎样的组合快捷键最高效。
如此坚持一段时间之后,编码的过程会很流畅,速度就会大大提高。
这是一个很老的问题了,经常在论坛上看到,很多人也写了相关的文章。我在这方面拥有较多的经验,我也谈一下我的看法吧。
我曾经实现过金蝶EAS BOS的多数据支持引擎,脚本引擎,也作过O-R Mapping的预研工作,曾经对多个O-R Mapping产品做过研究。
C++、Java、C#都源自Algol,这系列语言也称为Imperative语言,中文翻译为命令式语言。命令式语言建立在冯*诺依曼体系结构上,程序员必须处理变量管理、变量复制。这样的结果是增加了执行的效率,但带来了程序开发的艰苦。
LISP、Schema、Haskell等语言属于函数式语言,函数式语言基于数学函数,不使用变量或者赋值语句产生结果,使用函数的应用、条件表示和递归作为执行控制。函数式语言是更高级的程序设计语言,和命令式语言相比,编写更少的代码,更高的开发效率,这是函数式语言的明确有点。很多编程技术都首先应用于函数式语言,例如范型、垃圾收集等。很多函数式语言都增加了一些命令式语言的特征,增加效率和易用性。
SQL语言是一个领域专用语言,专门用于处理关系数据。他具备一些函数式语言的特征,在处理关系数据方面非常直观和简介。在处理选择、投影、聚合、排序等等操作方面,显然比在Java或者C#中要方便得多。SQL也是易学易用。很多非程序员都掌握SQL,在金蝶,大多需求人员都熟练掌握SQL。SQL的解释需要损耗一定的性能,在对性能极端要求的情况下,通常不使用关系数据库。例如Google Account采用Berkeley DB等。
现在关系数据库已经发展很成熟,数据库的一些技术发展得很好,包括事务等,其中一些从数据库中发展起来的技术,还应用到操作系统中了。在前些年面向对象技术狂热的时候,作了很多面向对象数据库的研究,但是都没有取得较大的成功。在主流的关系数据库中,大多都包括了面向对象的支持,例如Oracle、Sybase,都具备了很丰富的面向对象功能,但是很少任用。
现在有人跟我说起db4o这样的数据库,我认为db4o不会取得成功,因为他在错误的方向发展。
现在关系数据库最流行,最流行的应用开发语言包括Java、C#等,都是面向对象命令式语言。开发语言需要访问关系数据库,需要转换,转换的过程比较繁琐,于是就诞生了O-R Mapping技术。这是一种妥协的结果,面向对象技术在数据库领域无法取得成功,在面向对象开发语言中,就需要一种对象-关系映射技术。我相信,这种妥协产生的技术,会越来越流行。
我也认为,这是一个正确的选择。就如高级语言不会尝试取代汇编,无论高级语言有多好,最多在其上增加抽象层。例如Java使用bytecode、C#使用IL这样,使用一种抽象层,而不是尝试取代汇编。
O-R Mapping技术除了简单映射之外,需要一种OQL,混合SQL和面向对象特征,提供映射方便的同时,保留关系数据库提供的强大功能。例如聚合、排序等关系运算必须在OQL中提供。由于程序中的返回结果,有时不需要映射成对象,所以OQL必须提供另外一种功能,数据查询。很多O-R Mappping产品都提供了基于对象的OQL和基于数据的OQL。
这导致包括三个部分:
1、应用程序使用OQL
2、O-R Mapping解释或者编译OQL
3、对象数据库负责数据处理
如果O-R Mapping使用解释的方式处理OQL,则会包括产生SQL、组装对象等操作。效率通常都不会很好,现在的O-R Mapping产品基本都是基于解释的方式处理。
如果O-R Mapping使用编译的方式,可以优化产生SQL,动态创建SQL存储过程,减少SQL交互的过程,能够获得很好的性能,可以做到比绝
大多数人直接使用SQL性能都要好。我曾经做过一个实验性的实现,取得很好的效果,可惜由于种种原因,没有继续下去。
我认为,下一代的O-R Mapping产品,都将会采用编译的方式,因为在很多情形下,特别是复杂对象的处理方面,可以有大幅度的性能提升,在某些场景下,可以数倍甚至数十倍的性能提升。
一直不是很看好Hibernate,因为其主要作者gavin对编译技术不了解,2.x版本的HQL语法很搞笑,实现也是很搞笑的,简单的文本替换,看到让人目瞪口呆。3.0之后加入了HQL的AST(抽象语法树),但这不是他本人的做的,其他爱好者加入进来,3.1还是没有很好融合进来。之后的版本我就没有继续关注了。
我觉得O-R Mapping完全就是一种编译技术,不懂编译技术的人去作这件事清总会有些不妥。这不单是Hibernate的问题,也是其他O-R Mapping产品的问题。
我的观点:
1、Java、C#、C++等语言在处理关系数据方面没有优势。SQL是关系数据处理的领域专用语言(DSL),更适合处理关系数据,提供强大的功能。
2、关系数据是主流,希望应用开发人员使用O-R Mapping,而不懂关系数据库,这是不现实的。
3、O-R Mapping技术还有很大发展空间,以后在功能、性能等方面都会有重大提升,最终成熟。
文章来源:
http://www.cnblogs.com/jobs/archive/2007/04/23/723297.html
在操作系统中,有两种不同的方法提供线程支持:用户层的用户线程,或内核层的内核线程。
其中用户线程在内核之上支持,并在用户层通过线程库来实现。不需要用户态/核心态切换,速度快。操作系统内核不知道多线程的存在,因此一个线程阻塞将使得整个进程(包括它的所有线程)阻塞。由于这里的处理器时间片分配是以进程为基本单位,所以每个线程执行的时间相对减少。
内核线程由操作系统直接支持。由操作系统内核创建、调度和管理。内核维护进程及线程的上下文信息以及线程切换。一个内核线程由于I/O操作而阻塞,不会影响其它线程的运行。
Java线程的实现是怎样的呢?我们通过SUN Java 6的源码了解其在Windows和Linux下的实现。
在Windows下的实现,os_win32.cpp中
// Allocate and initialize a new OSThread
bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
unsigned thread_id;
// Allocate the OSThread object
OSThread* osthread = new OSThread(NULL, NULL);
if (osthread == NULL) {
return false;
}
// Initial state is ALLOCATED but not INITIALIZED
{
MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);
osthread->set_state(ALLOCATED);
}
// Initialize support for Java interrupts
HANDLE interrupt_event = CreateEvent(NULL, true, false, NULL);
if (interrupt_event == NULL) {
delete osthread;
return NULL;
}
osthread->set_interrupt_event(interrupt_event);
osthread->set_interrupted(false);
thread->set_osthread(osthread);
if (stack_size == 0) {
switch (thr_type) {
case os::java_thread:
// Java threads use ThreadStackSize which default value can be changed with the flag -Xss
if (JavaThread::stack_size_at_create() > 0)
stack_size = JavaThread::stack_size_at_create();
break;
case os::compiler_thread:
if (CompilerThreadStackSize > 0) {
stack_size = (size_t)(CompilerThreadStackSize * K);
break;
} // else fall through:
// use VMThreadStackSize if CompilerThreadStackSize is not defined
case os::vm_thread:
case os::pgc_thread:
case os::cgc_thread:
case os::watcher_thread:
if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K);
break;
}
}
// Create the Win32 thread
//
// Contrary to what MSDN document says, "stack_size" in _beginthreadex()
// does not specify stack size. Instead, it specifies the size of
// initially committed space. The stack size is determined by
// PE header in the executable. If the committed "stack_size" is larger
// than default value in the PE header, the stack is rounded up to the
// nearest multiple of 1MB. For example if the launcher has default
// stack size of 320k, specifying any size less than 320k does not
// affect the actual stack size at all, it only affects the initial
// commitment. On the other hand, specifying 'stack_size' larger than
// default value may cause significant increase in memory usage, because
// not only the stack space will be rounded up to MB, but also the
// entire space is committed upfront.
//
// Finally Windows XP added a new flag 'STACK_SIZE_PARAM_IS_A_RESERVATION'
// for CreateThread() that can treat 'stack_size' as stack size. However we
// are not supposed to call CreateThread() directly according to MSDN
// document because JVM uses C runtime library. The good news is that the
// flag appears to work with _beginthredex() as well.
#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION
#define STACK_SIZE_PARAM_IS_A_RESERVATION (0x10000)
#endif
HANDLE thread_handle =
(HANDLE)_beginthreadex(NULL,
(unsigned)stack_size,
(unsigned (__stdcall *)(void*)) java_start,
thread,
CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION,
&thread_id);
if (thread_handle == NULL) {
// perhaps STACK_SIZE_PARAM_IS_A_RESERVATION is not supported, try again
// without the flag.
thread_handle =
(HANDLE)_beginthreadex(NULL,
(unsigned)stack_size,
(unsigned (__stdcall *)(void*)) java_start,
thread,
CREATE_SUSPENDED,
&thread_id);
}
if (thread_handle == NULL) {
// Need to clean up stuff we've allocated so far
CloseHandle(osthread->interrupt_event());
thread->set_osthread(NULL);
delete osthread;
return NULL;
}
Atomic::inc_ptr((intptr_t*)&os::win32::_os_thread_count);
// Store info on the Win32 thread into the OSThread
osthread->set_thread_handle(thread_handle);
osthread->set_thread_id(thread_id);
// Initial thread state is INITIALIZED, not SUSPENDED
{
MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);
osthread->set_state(INITIALIZED);
}
// The thread is returned suspended (in state INITIALIZED), and is started higher up in the call chain
return true;
}
可以看出,SUN JVM在Windows下的实现,使用_beginthreadex来创建线程,注释中也说明了为什么不用“Window编程书籍推荐使用”的CreateThread函数。由此看出,Java线程在Window下的实现是使用内核线程。
而在Linux下又是怎样的呢?
在os_linux.cpp文件中的代码摘录如下:
# include <pthread.h>
bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
assert(thread->osthread() == NULL, "caller responsible");
// Allocate the OSThread object
OSThread* osthread = new OSThread(NULL, NULL);
if (osthread == NULL) {
return false;
}
// set the correct thread state
osthread->set_thread_type(thr_type);
// Initial state is ALLOCATED but not INITIALIZED
osthread->set_state(ALLOCATED);
thread->set_osthread(osthread);
// init thread attributes
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// stack size
if (os::Linux::supports_variable_stack_size()) {
// calculate stack size if it's not specified by caller
if (stack_size == 0) {
stack_size = os::Linux::default_stack_size(thr_type);
switch (thr_type) {
case os::java_thread:
// Java threads use ThreadStackSize which default value can be changed with the flag -Xss
if (JavaThread::stack_size_at_create() > 0) stack_size = JavaThread::stack_size_at_create();
break;
case os::compiler_thread:
if (CompilerThreadStackSize > 0) {
stack_size = (size_t)(CompilerThreadStackSize * K);
break;
} // else fall through:
// use VMThreadStackSize if CompilerThreadStackSize is not defined
case os::vm_thread:
case os::pgc_thread:
case os::cgc_thread:
case os::watcher_thread:
if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K);
break;
}
}
stack_size = MAX2(stack_size, os::Linux::min_stack_allowed);
pthread_attr_setstacksize(&attr, stack_size);
} else {
// let pthread_create() pick the default value.
}
// glibc guard page
pthread_attr_setguardsize(&attr, os::Linux::default_guard_size(thr_type));
ThreadState state;
{
// Serialize thread creation if we are running with fixed stack LinuxThreads
bool lock = os::Linux::is_LinuxThreads() && !os::Linux::is_floating_stack();
if (lock) {
os::Linux::createThread_lock()->lock_without_safepoint_check();
}
pthread_t tid;
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
pthread_attr_destroy(&attr);
if (ret != 0) {
if (PrintMiscellaneous && (Verbose || WizardMode)) {
perror("pthread_create()");
}
// Need to clean up stuff we've allocated so far
thread->set_osthread(NULL);
delete osthread;
if (lock) os::Linux::createThread_lock()->unlock();
return false;
}
// Store pthread info into the OSThread
osthread->set_pthread_id(tid);
// Wait until child thread is either initialized or aborted
{
Monitor* sync_with_child = osthread->startThread_lock();
MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag);
while ((state = osthread->get_state()) == ALLOCATED) {
sync_with_child->wait(Mutex::_no_safepoint_check_flag);
}
}
if (lock) {
os::Linux::createThread_lock()->unlock();
}
}
// Aborted due to thread limit being reached
if (state == ZOMBIE) {
thread->set_osthread(NULL);
delete osthread;
return false;
}
// The thread is returned suspended (in state INITIALIZED),
// and is started higher up in the call chain
assert(state == INITIALIZED, "race condition");
return true;
}
Java在Linux下的线程的创建,使用了pthread线程库,而pthread就是一个用户线程库,因此结论是,Java在Linux下是使用用户线程实现的。
MessageDigest的选择好多,包括MD2、MD4、MD5、SHA-1、SHA-256、RIPEMD128、RIPEMD160等等。我们如何选择呢?
选择考虑在两个方面:安全、速度。
MD2很安全,但是速度极慢,一般不用。
速度方面,最快的是MD4,MD5比SHA-1快
速度排名:MD4 > MD5 > RIPEMD-128 > SHA-1 > REPEMD-160
按照《应用密码学手册》提供的表格数据为:
MD4 长度 128 相对速度 1
MD5 长度 128 相对速度 0.68
REPEMD-128 长度 128 相对速度 0.39
SHA-1 长度 160 相对速度 0.29
REPEMD-160 长度 160 相对速度 0.24
我亲自测试的结果和《应用密码学手册》提供的数据接近。
MD4已经很早证明有缺陷,很少使用,最流行的是MD5和SHA-1,但MD5和SHA1也被王小云找到碰撞,证实不安全。
传说SHA-1比MD5要安全,但是SHA-1有美国国家安全局的背景,有人怀疑这个算法背后有不可告人的秘密,我也是阴谋论者之一,倾向选择MD5而不是SHA-1。王小云找到SHA-1碰撞之后,可以说传说的谣言破灭了,而且MD5速度要比SHA-1快差不多一倍,没有什么理由选择SHA-1。
----------------------------------
在Java的现实环境中是怎样?
在SUN的JCE实现中,只提供了MD2、MD5、SHA-1,SHA-256等常用的MessageDigest算法。
开源的JCE实现bouncycastle则提供了众多的实现,包括MD整个系列,SHA整个系列,RIPEMD整个系列。
很多的开源项目都带一个bcprov-jdk14.jar的包,可以说bouncycastle应用很广泛。SUN公司的一些项目也用了bouncycastle,例如JXTA。
但实际上,SUN的实现包括了MD4,但你需要这样使用:
MessageDigest md = sun.security.provider.MD4.getInstance();
但是,JDK带实现性能要比bouncycastle性能好得多,相差速度通常超过一倍以上,我测试过MD5、SHA1和MD4,其性能差别都是类似,一倍多。
比较的结论:
bouncycastle开源免费,提供算法多,但速度较慢
SUN JCE提供的实现,包括了流行常用算法,速度很快,同类型算法比bouncycastle要快一倍以上。
----------------------------------
结论:
又要安全又要速度,选择MD5
追求安全,不在意速度,相信传说,不相信阴谋论,选择SHA系列
追求速度,安全次之,可以选择MD4。
----------------------------------
现实例子:
emule采用MD4和SHA-1两种结合使用
apache之类的技术网站,提供下载的文件,同时提供一个校验文件.md5
不是使用每连接一线程的技术,而是使用多路复用技术。
作了一个分配算法。第一个HTTP Request返回取得ContentLength之后,如果使用多个连接下载,则需要一个分配算法,分配每个Request所对应的Range。分配的部分可能是一个连续的块,例如bytes=100-999,也可能是一些碎块,例如bytes=500-600,700-800,850-999。为此,我做了一个数据结构,其提供的功能类似java.util.BitSet,也支持and、or等操作。
实现了对ContentType为multipart/bytes的HTTP Message Body的解释。如果发送HTTP Request,Range为多个不连续的部分,返回的HTTP Message,就会是multipart,每个part都会包括一个Head和一个Body,需要一个解析器。
下一步就是把HTTP下载加入P2P下载中!
最近编码更流畅了。原因包括:
1) 绝大多数时候纯键盘操作,Eclipse 200多个快捷键,我熟练使用绝大部分,编码的过程,如同行云流水般。
2)掌握了更多的解决问题的办法,有了更广的知识面,编码时,信手拈来。最近一年里,掌握了很多知识,包括并发、网络、操作系统等等方面的知识。
3)组织代码的能力更强了,最近对于大型复杂的程序,组织代码的能力更强了,组织程序的能力包括,更好的结构,更好的扩展性,可测试性,可管理性等等。
a) 在编码的过程中,使得更多的模块可以单独于整个环境独立测试
b) 精心处理过的LOG,使得代码更容易跟踪,排错。
c) 复杂的模块,附带监控管理界面,使得排错和优化都更为方便。
d) 使用制作状态转换表的手段,清晰化复杂的状态处理。在前些年设计/实现工作流引擎时,就开始使用状态转换表描述状态机,但现在面临的状态机复杂。
贴图的实现方式为:
1、把剪切板中的图片存在本地的SendingImages目录,存放的格式使用PNG,当然可以其他格式,但是PNG格式更小。
2、使用MD5算法产生一个ImageID。当然可以使用SHA1等其他算法
3、把imageID发送remote peer
4、当remote peer收到imageID时,检查本地ReceivedImage目录,如果已经存在,显示图片,不存在则发送一个RequestImage请求,并在聊天记录中显示一个等待信息(为一个GIF动画)。
5、本地Peer收到RequestImage请求之后,发送图片数据。如果图片大于64K,则分块发送。
6、remote peer收到图像数据之后,进行校验,看是否正确。
7、校验通过后,把图片在聊天面板上显示(替换等待图片)
预定义表情的实现很简单,自定义表情的实现和贴图实现一致,只是少了从剪贴板保存图片的过程。