2007年3月20日

          新寫了一個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 溫少的日志 閱讀(6410) | 評論 (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 溫少的日志 閱讀(1534) | 評論 (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 溫少的日志 閱讀(1669) | 評論 (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 溫少的日志 閱讀(677) | 評論 (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 溫少的日志 閱讀(2487) | 評論 (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 溫少的日志 閱讀(3663) | 評論 (8)編輯 收藏
           
               摘要: 我們在開發中,經常需要遍歷一個目錄下的所有文件,常用的辦法就是使用一個函數遞歸遍歷是常用的辦法。但是遞歸函數的缺點就是擴展不方便,當然你對這個函數加入一個參數FileHandler,這樣擴展性稍好一些,但是仍然不夠好,比如說,不能根據遍歷的需要中途停止遍歷,加入Filter等等。我實現了一個FileIterator,使得遍歷一個目錄下的文件如何遍歷一個集合中的元素一般操作。  閱讀全文
          posted @ 2008-06-05 07:56 溫少的日志 閱讀(1902) | 評論 (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 溫少的日志 閱讀(1763) | 評論 (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 溫少的日志 閱讀(3694) | 評論 (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 溫少的日志 閱讀(1002) | 評論 (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 溫少的日志 閱讀(5268) | 評論 (19)編輯 收藏
           



          中國最大的在線記賬及商務管理平臺—金蝶“友商網”(
          www.youshang.com)正式上線!請立刻體驗!
          posted @ 2007-11-29 15:42 溫少的日志 閱讀(1478) | 評論 (13)編輯 收藏
           
               摘要: 本文描述一種ID生成算法  閱讀全文
          posted @ 2007-11-16 07:09 溫少的日志 閱讀(2738) | 評論 (8)編輯 收藏
           
               摘要: 本文介紹流行的非阻塞算法關鍵思想Compare And Set在數據庫開發中的應用  閱讀全文
          posted @ 2007-11-13 06:33 溫少的日志 閱讀(1464) | 評論 (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 溫少的日志 閱讀(1207) | 評論 (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 溫少的日志 閱讀(2932) | 評論 (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 溫少的日志 閱讀(915) | 評論 (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 溫少的日志 閱讀(580) | 評論 (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 溫少的日志 閱讀(1408) | 評論 (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 溫少的日志 閱讀(2029) | 評論 (2)編輯 收藏
           
          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,他為我們帶了一個如此完美的并發庫!
          posted @ 2007-04-26 07:08 溫少的日志 閱讀(3294) | 評論 (0)編輯 收藏
           

          一旦方案想清楚,剩余部分的工作效率瓶頸就在于你的手速了。最近一直看起點中文網上的《師士傳說》,主角葉重一個強項就是手速。

          最基本的就是盲打。不會盲打的通常屬于“編碼低能兒”。身邊也有不會盲打的朋友,他們通常都有一個問題,就是眼高手低,說說還行,動手就不行。當然他們能夠在IT研發領域還混得很好,是因為在其他方面擁有優秀的能力。

          熟練掌握快捷鍵是關鍵。鍵盤和鼠標之間通常有較大的距離,手經常在鍵盤和鼠標之間移動,會降低效率,也容易導致疲勞,用鼠標過多,也容易導致齷齪的鼠標手。解決這個問題的辦法,就是純鍵盤操作,其實很多IDE的快捷鍵功能強大,足以讓你純鍵盤操作,高效率編碼。

          我比較熟悉的IDE是Eclipse,就以Eclispse來說吧。

          Eclipse的keys列表中,屬于Eclipse本身有180多個快捷鍵,要提高編碼速度,就應該熟練使用其中絕大多數。

          練習的辦法:
          1、在Windows/Preferences/General/keys中,使用Export,把快捷鍵導出,導出的格式是csv格式,Windows下可以用Excel直接打開,Linux下可以用OpenOffice打開,打開時選擇分隔符為“,”。

          2、挨個練習使用。每天練習一部分,反復練習,堅持一段時間。

          3、開始的時候,把鼠標放到一個不方便使用的角落,盡量不要讓自己用鼠標。

          4、快捷鍵的組合使用需要加強訓練。在不同場景下,認真考慮用怎樣的組合快捷鍵最高效。

          如此堅持一段時間之后,編碼的過程會很流暢,速度就會大大提高。



          posted @ 2007-04-25 07:41 溫少的日志 閱讀(3227) | 評論 (25)編輯 收藏
           

          這是一個很老的問題了,經常在論壇上看到,很多人也寫了相關的文章。我在這方面擁有較多的經驗,我也談一下我的看法吧。

          我曾經實現過金蝶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技術還有很大發展空間,以后在功能、性能等方面都會有重大提升,最終成熟。


          溫少 2007-04-23 08:18 發表評論

          文章來源:http://www.cnblogs.com/jobs/archive/2007/04/23/723297.html
          posted @ 2007-04-23 08:23 溫少的日志 閱讀(1186) | 評論 (2)編輯 收藏
           

          在操作系統中,有兩種不同的方法提供線程支持:用戶層的用戶線程,或內核層的內核線程。

          其中用戶線程在內核之上支持,并在用戶層通過線程庫來實現。不需要用戶態/核心態切換,速度快。操作系統內核不知道多線程的存在,因此一個線程阻塞將使得整個進程(包括它的所有線程)阻塞。由于這里的處理器時間片分配是以進程為基本單位,所以每個線程執行的時間相對減少。

          內核線程由操作系統直接支持。由操作系統內核創建、調度和管理。內核維護進程及線程的上下文信息以及線程切換。一個內核線程由于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, truefalse, 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下是使用用戶線程實現的。





          溫少 2007-04-18 05:28 發表評論
          posted @ 2007-04-18 05:28 溫少的日志 閱讀(954) | 評論 (4)編輯 收藏
           

          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



          溫少 2007-04-14 17:02 發表評論
          posted @ 2007-04-14 17:02 溫少的日志 閱讀(498) | 評論 (0)編輯 收藏
           
          JSR-000203 More New I/O APIs for the Java Platform - Early Draft Review

          http://jcp.org/aboutJava/communityprocess/edr/jsr203/index.html


          API的Early Draft Review出來了,就意味很快就要真的出來啦!!

          以下是其文檔的一個Sample

           static class IOTransaction {
                
          public ByteBuffer buffer() {  }
                
          public long position() {  }
                
          public long updatePosition(int transferred) {  }
            }

            
          static class WriteHandler implements CompletionHandler<Integer> {

                
          public WriteHandler(AsynchronousFileChannel ch) {  }

                
          private AsynchronousFileChannel channel() {  }

                
          public void completed(IoFuture<Integer> result) {
                    
          int bytesTransferred;
                    
          try {
                        bytesTransferred 
          = result.getNow();
                    } 
          catch (ExecutionException x) {  }
           
                    IOTransaction transaction 
          = (IOTransaction)result.attachment();   
                    ByteBuffer buffer 
          = transaction.buffer();
                    
          if (buffer.remaining() > 0) {
                        
          long position = transaction.updatePosition(bytesTransferred);
                        channel().write(buffer, position, transaction, 
          this);
                    }
                }
            }

            FileReference file 
          = 
            List
          <IOTransaction> transactionList = 
            
            AsynchronousFileChannel ch 
          = AsynchronousFileChannel.open(file, OpenFlag.WRITE);

            WriteHandler handler 
          = new WriteHandler(ch);

            
          for (IOTransaction transaction: transactionList) {
                
          // use the transaction as the attachment
                ch.write(transaction.buffer(), transaction.position(), transaction, handler);
            }


          就是我最近很需要的東西,一個異步I/O的實現,十分期待中!!



          溫少 2007-04-14 12:15 發表評論
          posted @ 2007-04-14 12:15 溫少的日志 閱讀(267) | 評論 (0)編輯 收藏
           

          不是使用每連接一線程的技術,而是使用多路復用技術。

          作了一個分配算法。第一個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下載中!


          溫少 2007-04-12 01:37 發表評論
          posted @ 2007-04-12 01:37 溫少的日志 閱讀(384) | 評論 (0)編輯 收藏
           

          最近編碼更流暢了。原因包括:

          1) 絕大多數時候純鍵盤操作,Eclipse 200多個快捷鍵,我熟練使用絕大部分,編碼的過程,如同行云流水般。

          2)掌握了更多的解決問題的辦法,有了更廣的知識面,編碼時,信手拈來。最近一年里,掌握了很多知識,包括并發、網絡、操作系統等等方面的知識。

          3)組織代碼的能力更強了,最近對于大型復雜的程序,組織代碼的能力更強了,組織程序的能力包括,更好的結構,更好的擴展性,可測試性,可管理性等等。
            a) 在編碼的過程中,使得更多的模塊可以單獨于整個環境獨立測試
            b) 精心處理過的LOG,使得代碼更容易跟蹤,排錯。
            c) 復雜的模塊,附帶監控管理界面,使得排錯和優化都更為方便。
            d) 使用制作狀態轉換表的手段,清晰化復雜的狀態處理。在前些年設計/實現工作流引擎時,就開始使用狀態轉換表描述狀態機,但現在面臨的狀態機復雜。




          溫少 2007-03-31 02:42 發表評論
          posted @ 2007-03-31 02:42 溫少的日志 閱讀(267) | 評論 (0)編輯 收藏
           

          貼圖的實現方式為:

          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、校驗通過后,把圖片在聊天面板上顯示(替換等待圖片)


          預定義表情的實現很簡單,自定義表情的實現和貼圖實現一致,只是少了從剪貼板保存圖片的過程。



          溫少 2007-03-30 22:01 發表評論
          posted @ 2007-03-30 22:01 溫少的日志 閱讀(229) | 評論 (0)編輯 收藏
           
          上一篇博客寫了我一些關于P2P下載以及平臺的思考,有這樣的思考,是因為我正在做一件這樣的事情。

          我介紹一下我正在做的事情吧:

          1、基于JXTA,我崇拜Bill Joy,學習JXTA就是因為我崇拜他,之后覺得這個技術很棒。但是JXTA存在一些用戶不友好的地方,包括JXTA的ConfigDialog和DialogAuthenticator是十分用戶不友好的,我重寫了這些部分。雖然是一些無關痛癢的地方,但是可以改變用戶體驗,提高用戶友好性。

          2、簡單的插件機制,我做了一個簡單的插件系統,Application啟動之后挨個裝載服務,UI也是服務之一,UI也是基于插件的,在微內核框架流行的今天,使用一個簡單的插件機制似乎不是太好,等過一段時間之后考慮使用osgi替代之。

          3、提供了兩個功能,聊天和文件共享下載。這兩個功能分別表現為兩個JXTA的Service。

          4、聊天功能。目前還比較簡單,只實現了不帶格式的文本聊天,但是我隨后會加入帶格式的文本聊天,也將會加入類似騰訊QQ那樣的貼圖支持,自定義表情支持,騰訊QQ的實現很巧妙,但并不困難。四月初的版本就有可能實現之。

          5、共享和下載。目前實現了文件和文件夾的共享。其中包括了高級智能錯誤檢測(AICH)等。傳輸協議參考了BT和emule的協議。在界面中還實現對DragAndDrop支持,從Windows Explore中拖一個文件到目錄共享的面板,即開始共享該文件。

          6、存儲信息采用apache的Derby數據庫。我很喜歡Berkeley DB,Berkely DB高效簡潔,但是License不開放。我最終還是采用Derby了,采用Derby將會帶來一系列好處,SQL支持、JDBC支持等等,License無限制等等。擴展的應用基于其上也十分方便。由于我曾經開發過多數據庫支持引擎KSQL,在KSQL上增加支持Derby的翻譯是很容易的事情。如此一來,可能存儲引擎部分,將有可能擴展到KSQL目前所支持的多種數據庫,包括嚴格測試過的Oracle、DB2、MS SQL Server,還有經過簡單測試支持Sybase、PostgreSQL、MySQL。

          7、最近的JXTA Java SE 2.5版本,使用了nio來管理連接,也就說,使用了多路復用的技術,使得每個Peer創建大量連接成為可能,例如Windows下默認最大的多路復用支持1024個連接。而Linux下,java nio是使用epoll實現的,并發性能將更好,這對于聚合點來說很重要。普通的Peer部署在Linux下可能較少,但是聚合點部署在Linux完全是可能的。

          8、使用Swing做界面,使用Java 6 SE的Swing,做了系統托盤Tray的支持等等。由于Swing的UI設計工具很不穩定,最終完全手工編寫UI部分代碼,雖然辛苦,但是代碼簡潔,不同UI Designer生成的那樣。

          9、我期望4月初發布一個版本,提供一個基本可用的版本。

          10、我是從1月初開始學習JXTA的,到現在還不滿3個月,其中還包括過年回家休息等等,玩游戲沉迷等等,但總的來說,我對這個學習速度很滿意。不過其中感覺最爽的是,在這個過程中,編碼時,基本純鍵盤操作,不用鼠標,如行云流水一邊,十分流暢,工作效率高,人也舒服。




          溫少 2007-03-25 06:39 發表評論
          posted @ 2007-03-25 06:39 溫少的日志 閱讀(273) | 評論 (0)編輯 收藏
           
          1、使用多路復用或者異步I/O模型,這本是服務器段常用的技術,但在P2P應用,每臺機器既是服務器,又是客戶端,共享了一個十分受歡迎的文件,可能會有很多希望連接者,或者你下載一個受歡迎文件時,可能搜索到數百上千的Peer,此時就很有必要采用多路復用或者異步I/O技術,降低應用程序所占用的資源。

          2、支持傳統的協議,包括HTTP和FTP,其實這兩種技術能夠和P2P網絡集成,其中一種辦法就是,在提供下載地址的同時提供一個種子文件下載,例如服務器中提供了ABC.rar文件,同時提供一個ABC.rar.md5文件允許下載,這樣P2P下載工具下載時,通過md5在P2P網絡中搜索更多的資源,這樣客戶能夠獲得更好的速度,服務器端也可能降低下載的網絡流量。

          3、流行的P2P網絡協議支持,包括BT和emule,這兩種都是公開協議了,都有開源的實現,可以參考并重寫,要支持并不困難。

          4、健壯性。如同emule一樣,將文件分塊(piece)的同時,把每一塊摘要一個piece_ID,將所有的piece_ID再摘要成一個總的ID,成為AICH。其實這也是一種很簡單的技術,實現起來并不困難,做法可以多種多樣。

          5、對大型局域網有特別支持。現實中,存在很多大型的局域網,局域網之間的擁有高速的帶寬。對局域網的特別支持辦法也有很多的,例如,類似BT那樣,在局域網里建立一個Tracker Server。若是基于JXTA,可以在局域網里部署聚合點(Rendezvous)

          6、支持P2P目錄共享,現在流行的P2P下載工具,都不支持以目錄為單位實現P2P共享和下載。其實支持P2P目錄共享也不困難,在提供共享時,提供一個目錄結構信息就可以了。目錄結構信息dir_info可以這樣記錄:子文件或子目錄路徑 偏移量 長度。當然把目錄壓縮然后提供下載也是可以的,不過這樣會浪費共享者的磁盤空間。目錄共享,要考慮共享之后文件進行修改,添加新文件等事情,使用dir_info能夠更好解決這種問題。

          7、關于通告。一個P2P共享資源(包括文件和目錄),應該包括三個ID:content_id、aich_id、dir_info_id。其中content_id是整個資源的摘要,aich_id是每塊id進行摘要產生的id,dir_info_id是dir_info的摘要id。
          content_id可用資源搜索,建議采用MD5摘要產生,因為現在很多網上提供下載的文件,都提供一個.md5后綴的校驗文件。
          aich_id用于校驗和智能恢復
          dir_info_id。如果計算content_id時,dir_info獨立計算,則需要提供dir_info_id,用于校驗dir_info。理論上dir_info可以作為content的一部分,但是我覺得dir_info獨立計算會帶來很多好處。

          8、關于傳輸。資源的傳輸,應該包括三部分,hashset的傳輸、dir_info的傳輸、內容數據的傳輸。內容傳輸是分塊傳輸的,我覺得采用BT的默認值256K一塊挺好的。每一塊(piece)摘要計算一個piece_id,所有的piece_id放在一起,就是一個hashset,hashset這個名字不大好,不直觀,但既然emule協議是這樣會說,我也這樣說好了。dir_info是可選的,文件共享不帶dir_info。

          9、P2P下載技術的應用范圍應該擴展,程序的安裝更新都應該加入P2P的支持,將會大大提高程序的用戶體驗。

          10、P2P的平臺應該具備良好的擴展性。當我們構建起一個龐大的P2P平臺時,不單單只是在其上共享文件,有很多應用可以部署在其上,包括現在很流行的P2P視頻,分布式計算等等。即時通訊也是可以構建在P2P網絡上的。面對眾多的應用需求,我們需要一個具備良好擴展性的協議,不應該像BT和emule那樣,除了下載,別無它用。可能基于JXTA是一種較好的選擇。

          11、安全。P2P網絡應該支持安全特性,一些團體,一些企業,需要限定范圍內共享資源。例如NASA的衛星數據共享項目SAXTA,采用JXTA,就是因為JXTA支持安全特性。我想很多的P2P應用場景,都需要安全,例如,企業只希望內部員工之間實現P2P資源共享等等。



          溫少 2007-03-25 05:47 發表評論
          posted @ 2007-03-25 05:47 溫少的日志 閱讀(232) | 評論 (0)編輯 收藏
           
          1、通用的唯一ID,使用MD5或者SHA1等摘要算法。
          2、需要引入類似emule AICH機制,防止惡意客戶端搗亂,或者用戶修改數據之后,無意上傳錯誤數據。
          3、引入文件結構。描述文件在整個共享內容中的位置,整個共享項包括那些文件等等。
          4、總共的ID應該包括:唯一ID、AICH_ID、文件結構摘要三個。如果使用JXTA的方式,需要在ContentAdv中包括這三個ID。
          5、如果采用類似BT種子文件的方式,可以把三個ID、AICH_HashSet、FILE_LIST_INFO全部放在一起。

          在做一個基于JXTA的實現,當然支持目錄P2P共享的,已經實現的差不多了,自我感覺很酷!!



          溫少 2007-03-20 01:15 發表評論
          posted @ 2007-03-20 01:15 溫少的日志 閱讀(334) | 評論 (1)編輯 收藏
           
          主站蜘蛛池模板: 雷州市| 康马县| 巴彦淖尔市| 普格县| 贵溪市| 岫岩| 临猗县| 锡林郭勒盟| 德令哈市| 宽甸| 哈尔滨市| 运城市| 娄底市| 区。| 民乐县| 叶城县| 尼勒克县| 阿瓦提县| 玛纳斯县| 蓬安县| 延安市| 南阳市| 额尔古纳市| 农安县| 商都县| 玉树县| 江门市| 调兵山市| 昭苏县| 十堰市| 聂拉木县| 洛阳市| 镇雄县| 家居| 南阳市| 巫溪县| 湟中县| 西城区| 河南省| 全州县| 新丰县|