竟
然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。