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