<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Evan's Blog

    Java, software development and others.

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      28 隨筆 :: 0 文章 :: 73 評論 :: 0 Trackbacks

    2006年3月5日 #

    最近一個項目需要考慮多語言的用戶,自然就想采用utf-8編碼,所有JSP頁面都修改完畢,一切運行正常,由于一直使用FF來檢測瀏覽器兼容性,今天不知怎的決定用IE來跑一下,發現所有表單提交的頁面都提示JS錯誤,查了一下,發現是一個驗證身份證的方法中有一個數組的初始化中保存了各個省市的名字,類似于:var area=['上海','北京']之類的形式。一般情況下,JS錯誤我都是在FF中用FireBug調試的,可偏偏在FF中一切正常,這下就傻眼了,注釋掉這個語句自然沒問題,可怎么也想不出這么正常的一個賦值怎么會弄出個瀏覽器兼容的問題出來。折騰了一個下午,突然想到,是不是編碼的問題啊,將這個JS用utf-8編碼另存,一切ok。

    另外,采用utf-8編碼之后,用WinMerge比較文件時,就會出來Information lost to encoding errors:之類的提示,心想,這個指定個編碼就可以了吧。在編輯=>選項中,果然看到代碼頁的指定,如是選擇指定代碼頁,輸入utf-8,點擊確定,被提示請輸入一個整數,估計這時候大多數人和我一樣傻眼,utf-8的代碼頁是多少?好在有Google,調整不同的關鍵字,終于找到清炒苦瓜的一篇文章中提到utf-8的代碼頁是65001,并且也是為了解決WinMerge的亂碼問題,可是改完之后,并沒有起作用,這次沒有去懷疑這個代碼頁是不是正確,再仔細看一下那篇文章,嗯,人家明明說用的是2.6.8嘛,檢查一下自己的版本,2.6.0,是不是版本問題呢?來到WinMerge的網站,發現竟然已經是2.6.12了,于是下載最新版本,然后將代碼頁改成65001,嗯,這下所有的中文注釋都乖乖出來了。
    posted @ 2008-01-05 21:41 Evan 閱讀(2833) | 評論 (1)編輯 收藏

         摘要: Apache反向代理設置  閱讀全文
    posted @ 2008-01-04 21:13 Evan 閱讀(5963) | 評論 (2)編輯 收藏

         摘要: 這個Blog,就象我久未清理的書桌,已經落了厚厚的一層灰塵,在這個新年的第一天,決定用這樣一個夠噱頭的標題,以一篇夠水的日志,來告訴自己,已經是2008年了......  閱讀全文
    posted @ 2008-01-01 13:22 Evan 閱讀(319) | 評論 (0)編輯 收藏

         摘要: 不要假裝我們是一個文明古國了,傳統早已割裂,我們是個無根的民族,精神一片荒蕪,偽造出的傳統只加劇了我們的虛偽,凸顯了我們的空洞與脆弱。  閱讀全文
    posted @ 2007-08-31 21:28 Evan 閱讀(222) | 評論 (0)編輯 收藏

         摘要: 你是不是從svn 1.2甚至更早的版本就開始用Subversion了?是不是在用svnserve做服務器?是不是很認真的讀了svn自帶的文檔?那么,很有可能,你也象我一樣,被它小小的忽悠了一把:)。  閱讀全文
    posted @ 2007-04-01 14:33 Evan 閱讀(3275) | 評論 (1)編輯 收藏

         摘要: 在網上兜到一篇文章,從講述一個譯者和一家出版社之間的糾紛,引申出了一些待人處世的道理,主要是關于一個人碰到不公平的事情的時候,應該怎樣辦,覺得頗有道理。什么樣的人才是有力量的人呢,也許是那些能合理處理不合理的事情,甚至能讓不合理的事情最終轉化為多贏局面的人吧。人生要真能達到這樣一種境界,該是怎樣一副海闊憑魚躍(管它波濤洶涌還是風平浪凈),天高任鳥飛(管它烏云閉日還是陽光普照)的從容畫面。  閱讀全文
    posted @ 2006-12-30 19:48 Evan 閱讀(1074) | 評論 (1)編輯 收藏

    好久沒有更新Blog了,看到還有人在關注這個Blog,很是慚愧。發現還是有人在使用Polarion的SVN Web Client,并且碰到了一些小問題,盡管我工作中幾乎不用這個客戶端,但當初安裝的版本依然還在,所以決定稍微研究一下,但愿能給喜好這款軟件的人一些幫助。

    1. 為什么老是提示“Your credentials are not correct. Please check them and try again. ”?
    這個多半是svn初學者常碰到的問題,尤其在使用HTTP協議的時候。一開始大家都用svnserve來做svn的服務器,自然配置的是conf中的passwd。但如果采用HTTP協議的話,就得使用Basci HTTP Authentication了,需要用Apache提供的htpasswd來管理用戶和口令。這個的配置在svn自帶的幫助文件中第6章“httpd, the Apache HTTP Server”一節中有比較詳細的解釋。但如果你沒有通篇看完,在Apache中沒有加上Require valid-user指令的話,那是允許匿名操作的。我想,你不愿意留下這樣的安全漏洞吧。

    2. 怎樣配置多個repository
    這個也是實際中需要的,當然,在它的readme中其實是說得很清楚的。但我們有些同志就是喜歡拿來就試,尤其是在有些類似于我這種其實語焉不詳的文章時,更是就喜歡照葫蘆畫瓢,而不去看最權威最原始的英文文檔了。要配置多個repository,以HTTP協議為例,在web.xml中要刪掉RepositoryUrl、Username、Password這3個參數的設置,然后加上ParentRepositoryDirectory參數,值自然是指向svn倉庫的父目錄了,比如http://localhost/svn/,這個東西又是哪里來的呢?自然需要在Apache中配置,用SVNParentPath來指定svn倉庫的父目錄,Apache會自動解析其下所有的倉庫的。這里要注意一下AuthzSVNAccessFile授權文件的寫法,這里將配置所有倉庫的存取權限,對于每個倉庫,需要用[倉庫名:/module]的方式來配置。

    3. 怎樣使用svn協議
    前面我一直用http協議做例子,實在是因為我在其2.5.0下沒有配置出來過svn協議:(。這次去其網站下載了個最新的nightly版本,發現其已經能夠支持http, svn, svn+ssh, ssl和proxy等6種協議了。看看其代碼結構,好象也發生了很大的變化,估計應該有比較大的改進。于是,用這個版本試了一把,呵呵,輕而易舉就把svn協議給連通了,包括多倉庫的情況。并且其還改進了原來設置父目錄地址時一定要在最后添加/的要求,估計原來在這個地方卡殼的朋友也不少吧:)。不過,新版本還是不支持中文文件名,看我以前的帖子自己改吧。

    Important: 由于svn webclient采用的javasvn(現已更名為svnkit)版本較低,用svn協議在提交老的文件時會失敗,但添加新的文件時沒有問題,所以,大家就不要再嘗試svn協議了。如果不采用SVN協議,則其官方發布的版本就沒什么問題了,已經有網友重新打包了一個解決了中文文件名的版本,到這里下載。(Updated: 2007.1.20)
    posted @ 2006-12-27 13:04 Evan 閱讀(3635) | 評論 (2)編輯 收藏

         摘要: 使用Jarkata FileUpload最新版本解決SVNWebClient提交中文文件名或注釋時出錯的問題。  閱讀全文
    posted @ 2006-05-14 20:11 Evan 閱讀(4836) | 評論 (11)編輯 收藏

         摘要: 在我的“[推薦]兩款好用的SVN Web Client”一文中曾經提及我所看好的sventon中文支持不好的問題,于是在其論壇中提了個問題,今天收到他們的郵件,稱在其最新的代碼中已經解決了中文的問題,今天下載更新后,發現的確解決了這個問題,呵呵,更新速度挺快的。  閱讀全文
    posted @ 2006-04-12 15:41 Evan 閱讀(1638) | 評論 (3)編輯 收藏

         摘要:   Everything is meaningless...假設你突然死掉,世界將會怎樣?世界將一樣絢麗,地球轉的一樣快,太陽系每天在宇宙中換一個位置。大海還是大海,波濤還是波濤,一樣的花開花落,潮起潮落。...你是多么可憐的小蟲子,在活著的短暫歲月里,在最美好的青春里,都不曾快樂過,用盡心力去聚集一大堆外在和心靈沒有關系的小東西,只是出于對未來的沒有信心,小小的心靈在接近熄滅的一天還在發出那個愚蠢的聲音,讓你忙碌,讓你憂慮的聲音:我要,我還要。天底下充滿了這樣的小蟲子,當一個離開了,又有一個來了,做著同樣的事情,洶涌著同樣的小小念頭,受著同樣的煎熬。于是上帝要感慨了:虛空的虛空,凡事都是虛空。已有的事,后必再有;已行的事,后必再行。日光之下,并無新事。
      ...
      不要憂慮“不要為明天憂慮,天上的飛鳥,不耕種也不收獲,上天尚且要養活它,田野里的百合花,從不憂慮它能不能開花,是不是可以開得和其它一樣美,但是它就自然的開花了,開得比所羅門皇冠上的珍珠還美。你呢,憂慮什么呢? 人比飛鳥和百合花貴重多了,上帝會棄你不顧嗎?”  閱讀全文
    posted @ 2006-04-09 22:13 Evan 閱讀(983) | 評論 (0)編輯 收藏

         摘要: 推薦兩款純Java的SVN Web Client軟件。其安裝使用均比ViewVC要好!  閱讀全文
    posted @ 2006-04-06 00:30 Evan 閱讀(19277) | 評論 (21)編輯 收藏

         摘要: 前幾天好奇,也剛好得了點空閑,然后就想看看ViewVC對Subversion的支持程度,于是就想裝個玩玩。好死不死的,在我的VMWare Workstation上剛好有個Windows Server 2003,心想,就它吧,可就這么一偷懶,折騰了我好幾天,最終還是只能算將就著把它給裝上了。  閱讀全文
    posted @ 2006-04-05 00:28 Evan 閱讀(7695) | 評論 (5)編輯 收藏

    常常能從galeer(嘎荔兒)的網絡日志里聽到一些比較好聽的歌曲,并且那些點評也不錯。今天聽到U2 - With or Withou You,首先,這首歌還真不錯;其次,不知是這首歌原來就帶的呢,還是galeer自己加上了這樣幾句詩:

    我雖然行過死蔭的幽谷,
    也不怕遭害;
    因為你與我同在;
    你的杖,你的竿,都安慰我。

    --《詩篇第二十三篇第四節》

    當然,一開始我不知道這是《舊約全書·詩篇》中的內容,于是就Google了一把,找到下面這篇文章《因為你與我同在》:


    我去一個鄉村教會講道,在接待的弟兄家休息。看見弟兄的兒子學習很用功,很是喜愛。我請小朋友將他的語文課本借給我看。這是人教版小學四年級語文課本。當中有一篇文章吸引了我——

    1989 年美國洛杉磯大地震,30萬人在不到4分鐘時間里受到不同程度的傷害。這其間一個小學的慘況讓人心酸。多少孩子的父母因在地震中痛失愛子而哀聲聞天,面對 地震后的學校廢墟只能絕望離去。但一個父親卻在廢墟中不斷地挖掘尋找自己那才七歲的叫阿曼達的兒子。救火隊長擋住他,“太危險了,隨時可能發生大爆炸,請 你離開。”別的學生家長也勸說,“太晚了,沒有希望了。”可這位父親不顧這些勸阻,在廢墟中不斷地尋找。就在他挖了整整八個小時后,父親聽見瓦礫堆底下傳 出他十分熟悉的聲音,“爸爸,是你嗎?”這時他發現有14個孩子在當中,因為這是在教室墻角的位置,當房頂塌下時架成大三角形,孩子們才沒被砸著。“我告 訴同學們不要害怕,說只要我爸爸活著就一定會來救我,也能救大家。因為你說過,不論發生什么,你總會和我在一起!”孩子對爸爸說。“爸爸,先讓我的同學出 去吧,我知道你會跟我在一起,我不怕。不論發生了什么,我知道你總會跟我在一起。”

    14個孩子獲救了!

    我們不禁會問,如果因為大爆炸的危險而絕望地放棄,如果這個父親和其他的家長一樣絕望地離開,如果阿曼達沒有“因為你與我同在”的信念,那結果又將如何?

    我想說的是,絕望讓生命失去,希望使生命存留。

    詩人說,“因為你與我同在。”(詩篇23:3)這樣的信心使詩人在人生的黑夜里依然有生命的曙光,在人生的冬天里可憑信宣告說,冬天來了,上帝的春天也不再遙遠。

    “因為我與你同在,要拯救你。這是耶和華說的。”(耶利米書1:8)

        阿曼達對父親單純的信念應一如我們對天上的父親執著的信仰。



    是啊,“絕望讓生命失去,希望使生命存留”,不過呢,對于我這個不信教的人來說,大可抱著象故事中的小阿曼達相信他父親只要活著就一定會來救他的希望一樣,也不一定非要抱著耶和華會來救我們的希望的。不過,不管信什么教,基督也好,佛陀也好,其實所有經典中的智慧都是值得學習的;當然,如果你在這個浮躁的物質社會中實在找不到可以依賴的東西時,信個把教也未嘗不可。
    posted @ 2006-04-01 21:33 Evan 閱讀(581) | 評論 (0)編輯 收藏

         摘要: SVN會取代CVS嗎?這個雖然不是我們這種小程序員能決定的大事,但學學總無妨吧,這里有一些我搜集的資料。  閱讀全文
    posted @ 2006-03-23 23:55 Evan 閱讀(3193) | 評論 (0)編輯 收藏

         摘要: 你是不是為了高的測試覆蓋度而在為每個函數添加多個測試方法,甚至連getX()和setX()都不放過呢?或者,你一看到覆蓋度達到100%的代碼,景仰之心就開始“有如滔滔江水綿綿不絕,又有如黃河泛濫,一發不可收拾”了呢?那么,你應該讀讀Andrew Glover在最近的developerWorks上發表的這篇文章。  閱讀全文
    posted @ 2006-03-18 23:23 Evan 閱讀(1204) | 評論 (0)編輯 收藏

         摘要: 《Java Threads》的第5章“Minimal Synchronization Techniques”,是這本書中到現在我認為最差的一章了,當然主要是我不喜歡JDK 1.5新推出的Atomic Class,而這一章卻花了不少篇章來介紹,且牽強地改造打字程序,又語焉不詳地指出這種改造的困難之處和可能帶來的副作用,但卻又不能從代碼的實際運行中看到這種副作用,很有誤導初學者的嫌疑。不過,我想,沒有哪個初學者會冒風險為了用Atomic Class而將原本簡單明了的算法改造得如此晦澀難懂,并且還有潛在的出錯風險。所以,對于Atomic Class,我建議跳過不讀,絕對沒有什么損失。不過對于其中“5.1.3 Double-Checked Locking”和“5.3 Thread Local Variables”這兩節倒要著重讀一讀,尤其是Thread Local,應該說是Java中一個比較重要的多線程工具。  閱讀全文
    posted @ 2006-03-11 23:11 Evan 閱讀(1656) | 評論 (0)編輯 收藏

         摘要: 快來看“洋本山”怎樣忽悠一個只想買一把錘子的人最后買了一個工具工廠的建造工廠的通用建造工廠。很別扭是吧,但如果你是個開發Web應用的Java程序員,你也許已經或者正在被忽悠。  閱讀全文
    posted @ 2006-03-11 17:04 Evan 閱讀(5785) | 評論 (23)編輯 收藏

    看到《Java Threads》第5章,介紹了JDK 1.5新加的一些所謂原子類(Atomic Classes),總感覺有點為原子而原子,實際操作中,又有多少人會為了少許的性能提升而刻意去用這些別扭的操作而放棄直觀的synchronize關鍵字或者Lock類呢?不過,這里不是想討論這個,而是當其用Atomic Classes來改造它的打字程序后,解釋用原子類只是保證類似遞增、遞減、賦值等操作的原子性,而不能保證其所在的方法一定是線程安全的,然后說,有可能按鍵事件的處理可能需要等待resetScore()處理完才能執行,而這會導致錯誤的評分(被當成多敲了鍵)。由于前幾章的內容相對比較簡單易懂,所以也沒有很仔細的運行那些例子。這里為了驗證一下,就運行了一下第4章的例子,然后發現,基本上第一次的評分總是錯的。這就引起了我的注意,因為,一般情況下,如果是race condition導致的錯誤是很難重現的,這么明顯的錯誤很可能是程序邏輯上的錯誤。仔細看了一下代碼,發現在start按鈕的事件處理方法里,有下面這樣一段代碼:
    startButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                    displayCanvas.setDone(false);
                    producer.setDone(false);
                    startButton.setEnabled(false);
                    stopButton.setEnabled(true);
                    feedbackCanvas.setEnabled(true);
                    feedbackCanvas.requestFocus();
                    score.resetScore();
                }
            });
    注意重置成績的調用放在了最后,此時,隨機生成字符的線程應該被喚醒并產生了第一個字符,然后,resetScore()將需要輸入的字符又設成了-1,所以,當你第一次輸入字符時,總是被認為是多擊了一次鍵而扣1分:(。既然這樣,那停止然后再啟動也應該會發生這個錯誤啊。而事實上的確是這樣。我想,這不應該看做是race condition吧,有什么樣的同步技術能夠避免這個問題呢?除非另外弄個標志,當成績沒有被重置前,不能產生第一個字符。當然,這是不需要的,只要將score.resetScore()放到第一句就可以了。

    然后又運行了第3章的例子,發現基本上沒有這個問題。難道第3章的代碼是正確的?打開源代碼一看,重置成績的方法還是放在最后,那這里為什么又是正確的呢?我想,大約是第3章的例子中,每次點擊start按鈕,都重新創建一個線程對象的原因吧。由于創建對象和初始化線程需要一定的時間,剛好給了主線程重置成績的機會。

    不知道作者有意為之呢,還是疏忽,不過,這樣的錯誤不能算是race condition的例子。
    posted @ 2006-03-09 22:11 Evan 閱讀(847) | 評論 (0)編輯 收藏

    第3章主要介紹了數據的同步(Data Synchronization),這一章則主要介紹線程之間的同步方法(Thread Notification),同樣包括傳統的wait-and-notify方法和JDK 1.5新推出的Condition Variable。在多線程編程中,數據同步和線程同步是兩個最基本也是最關鍵的部分。
    《Java Threads》一書中通過考察打字程序中當按下start和stop按鈕后,每次都創建兩個新的線程的效率問題來引入線程同步的概念,當然不是線程同步的主要用處。不過,教科書歸教科書,實際運用則又是另一回事。所以,通過書本學習語法,通過實踐來獲得運用經驗。

    4.1 Wait and Notify

    1. 等待/喚醒類似于Solaris或POSIX中的條件變量(conditon variables),或者Windows中的事件變量(evant variable)或者信號量(signal),用于某個/多個線程暫停等待某個條件的滿足,而該條件將由其它線程來設置的情況。
    2. 在Java中,就像每個對象有一個鎖之外,任何對象都可以提供等待/喚醒的機制。就像Java中的synchronized總是表示獲得某個具體對象的鎖一樣,wait和notify也總是等待某個具體的對象,并由該對象喚醒;同樣,獲得某個對象上的鎖不一定是該對象需要同步一樣,等待和喚醒的條件也不一定是與之綁定的對象。
    3. Java中wait-and-notify的幾個方法:
    void wait(): 使當前線程處于等待狀態,直到其它線程調用了nofity()或者notifyAll()方法為止。
    void wait(long timeout): 使當前線程處于等待狀態,直到其它線程調用了nofity()或者notifyAll()方法,或者超過了指定的時間(單位為ms)為止
    void wait(long timeout, int nanos):與wait(long)一樣,只是在某些JVM中可以精確到奈秒。
    void notify(): 喚醒一個正在等待該對象的線程。
    void notifyAll(): 喚醒所有正在等待該對象的線程。
    注意:任何等待和喚醒方法都必須在與之對應的對象的同步方法或同步塊里調用。即:wait-and-notify必須和與之對應的synchronized關鍵詞一起使用的。
    4. wait()和sleep()的主要區別:
      1) sleep()可以在任何地方調用,而wait()需要在同步方法或同步塊中調用;
      2) 進入wait()函數時,JVM會自動釋放鎖,而當從wait()返回即被喚醒時,又會自動獲得鎖;而sleep()沒有這個功能,因此如果在wait()的地方用sleep()代替,則會導致相應的nofity()方法在等待時不可能被觸發,因為notify()必須在相應的同步方法或同步塊中,而此時這個鎖卻被sleep()所在的方法占用。也就是說,wait-and-notify不可能與sleep()同時使用。

    4.1.1 The Wait-and-Notify Mechanism and Synchronization
    1. 這一節詳細的講解了wait-and-notify機制和synchronized的關系,主要是兩點:1)wait-and-notify必須和synchronized同時使用;2)wait()會自動釋放和獲取鎖;
    2. 這一節中舉了一個例子用來解釋可能存在當條件被不滿足時也有可能被喚醒的情況:
      1) 線程T1調用一個同步方法;
      2) T1檢測狀態變量,發現其不滿足條件;
      3) T1調用wait(),并釋放鎖;
      4) 線程T2調用另外一個同步方法,獲得鎖;
      5) 線程T3調用另外一個同步方法,由于T2獲得了鎖,所以處于等待狀態;
      6) T2修改狀態變量,使其滿足條件,并調用notify()方法;
      7) T3獲得鎖,然后處理數據,并將狀態變量又設置為不滿足條件的狀態;
      8) T3處理完畢返回;
      9) T1被喚醒,但實際上此時條件并不滿足。
    這個例子剛好印證了《Effective Java》中"Item 50: Never invoke wait outside a loop"和《Practical Java》中"實踐54:針對wait()和notifyAll()使用旋鎖(spin locks)"。即總是用下面這種方式來調用wait():
        synchronized(obj) {
    while(<condition does not hold>)
    wait();
    
    ... // Perform action appropriate to condition }
    或者象《Practical Java》中一樣:
        synchronized(obj) {
    while(<condition does not hold>) {
    try {
    wait();
    } catch (InterruptedException e) {}
    }

    ... // Perform action appropriate to condition }
    3. 調用wait()的線程T可能在以下幾種情況被喚醒:
      1) 其它線程調用了notify(),而剛好線程T得到了通知;
      2) 其它線程調用了notifyAll();
      3) 其它線程中斷了線程T;
      4) 由于JVM的原因,導致了spurious wakeup。

    4.1.2 wait(), notify(), and notifyAll()
    1. 正像多個線程等待同一對象上的鎖,當鎖釋放時,無法確定哪個線程會得到那個鎖一樣;當有多個線程在wait()時,當另外一個線程調用nofity()的時候,也不能確定哪個線程會被喚醒; 2. 因此在《Practical Java》的"實踐53:優先使用notifyAll()而非notify()"建議的一樣,結合實踐54,可以比較好的解決線程喚醒的問題。

    4.1.3 Wait-and-Notify Mechanism with Synchronized blocks
    再次強調必須在同一個對象的synchronized方法或塊內調用該對象上的wait和notify方法。

    4.2 Condition Variables

    1. 就像上面反復強調的一樣,wait-and-notify機制是與特定對象及其上的鎖是綁定在一起的,鎖和喚醒對象不能分開,這在某些情況下不是很方便;
    2. JDK 1.5提供Condition接口來提供與其它系統幾乎一致的condition variables機制;
    3. Condition對象由Lock對象的newCondition()方法生成,從而允許一個鎖產生多個條件變量,可以根據實際情況來等待不同條件;
    4. 該書的例子沒有什么特別的實際意義,但JDK 1.5文檔中提供了一個例子,能充分說明使用Condition Variables使得程序更加清晰易讀,也更有效率:
    class BoundedBuffer {
    final Lock lock = new ReentrantLock();
    final Condition notFull = lock.newCondition();
    final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count;

    public void put(Object x) throws InterruptedException {
    lock.lock();
    try {
    while (count == items.length)
    notFull.await();
    items[putptr] = x;
    if (++putptr == items.length) putptr = 0;
    ++count;
    notEmpty.signal();
    } finally {
    lock.unlock();
    }
    }
    public Object take() throws InterruptedException {
    lock.lock();
    try {
    while (count == 0)
    notEmpty.await();
    Object x = items[takeptr];
    if (++takeptr == items.length) takeptr = 0;
    --count;
    notFull.signal();
    return x;
    } finally {
    lock.unlock();
    }
    }
    }
    具體的說明請參考JDK 1.5的文檔。
    5. 除了用lock和await-and-signal來代替synchronized和wait-and-notify外,其語義和機制基本一樣。await()在進入前也會自動釋放鎖,然后再返回前重新獲得鎖;
    6. 使用Condition Variables的原因:
      1) 如果使用Lock對象,則必須使用condition variables;
      2) 每個Lock對象可以創建多個condition variable.
    posted @ 2006-03-06 22:21 Evan 閱讀(825) | 評論 (0)編輯 收藏

    除了C是在大學中系統學的外,C++、Java、Design Pattern等等等等都是在工作中趕鴨子上架的時候學的,雖然有邊做邊學的樂趣;但也有為了趕時間抓進度,只要解決了問題就好而淺嘗輒止的遺憾,也時有 遺忘的現象發生。近來得了點空閑,準備好好讀讀幾本或算經典或算時髦的書。所以,就有了這樣一個Blog,希望能將學習和工作中的一些小心得給記錄下來, 聊以敝帚自珍之需。
    posted @ 2006-03-05 23:25 Evan 閱讀(335) | 評論 (0)編輯 收藏

    任何一門支持多線程的語言中,多線程是都是一個讓人又愛又恨的東西。Java的多線程相對而言比其它語言要簡單一點,如果不是開發框架類或者系統級的程序,也許很少會碰到要明確碰到Java的多線程API,但事實上不等于你不用注意多線程安全的問題,尤其當你在開發Web程序的時候,在類中使用了靜態屬性(static fields)而不僅僅是對象屬性(instance fields)的時候,如果在壓力測試或者提交給用戶使用的時候,發生了一些不可重現的錯誤或者數據混亂的時候,那往往要查查這些使用了靜態屬性的類是否是多線程安全的了。當然,如果你專注于開發Web應用,并且很少涉及框架或核心模塊的開發,那也就基本上知道synchronized的關鍵字的應用就可以了。這也許就是Java多線程相對其它語言中多線程要簡單一點的原因。

    當然,這次我打算比較深入地來了解了解一下Java多線程開發的其它一些內容,那么找一本好的書是一個比 較好的開始。關于Java多線程開發的專著比較有名的大約是《Java Threads, 3rd Edition》和《Java Thread Programming》了,前者基于JDK 1.5(這個版本對多線程進行了很大的改進)進行介紹,并且指出了與以前版本的區別;而后者出版于1999年,是基于JDK 1.2進行講解的。所以呢,基本上采用第一本為主。同時也參考一下《Practical Java》和《Effective Java》的相關條目。

    這幾本書的封面如下,相關書的介紹可去Amazon查看一下:


    posted @ 2006-03-05 23:25 Evan 閱讀(583) | 評論 (1)編輯 收藏

    J2SE 5.0在多線程上做了很大的改進,提供了更多的API,包括:
    Atomic variables: A set of classes that provide threadsafe operations without synchronization
    Explicit locks: Synchronization locks that can be acquired and released programmatically
    Condition variables: Variables that can be the subject of a targeted notification when certain conditions exist
    Queues: Collection classes that are thread-aware Synchronization primitives: New classes that perform complex types of synchronization
    Thread pools: Classes that can manage a pool of threads to run certain tasks
    Thread schedulers: Classes that can execute tasks at a particular point in time

    在《Java Threads》一書中將其歸納為三類:
    1. 對現有功能的新實現;
    2. 提供了重要的多線程工具,如線程池(pool)和計劃(schedule);
    3. 最小化同步工具(Minimal synchronization utilities)。

    這些功能的妙處我現在自然是無法體會得到,但對于JDK 5.0中提供的這些多線程工具,會不會也遭遇JDK 1.4提供的Log API的命運,因敵不過第三方工具而成為擺設呢(至少目前我還在用Log4J,且其也沒有停止開發的跡象)?
    posted @ 2006-03-05 23:25 Evan 閱讀(421) | 評論 (0)編輯 收藏

    Chapter2: Thread Creation and Management

    2.1 What Is a Thread?

    介紹了什么是線程,以及線程(thread, multithread)與進程(process, mutltitask)的區別。其中的一個重要區別是對共享數據的訪問。進程可以共享操作系統級別的某些數據區域,如剪貼板;而線程是對程序自有的數據進行共享。進程之間對共享數據的存取方法是特殊的,因此自然能夠得到程序員的注意;而線程之間對共享數據的存取與對線程自己的數據的存取方法是一樣的,所以常常比較隱蔽,而被程序員忽略。其實,這也是多線程開發比較難的地方。所以,這一節最后說:"A thread, then, is a discrete task that operates on data shared with other threads.(線程就是一個在與其它線程共享的數據上進行操作的單獨任務。)"

    2.2 Creating a Thread

    1. 有兩種方法創建線程:使用Thread類或者Runnable接口
    2. 示例程序竟然是編一個打字游戲,所以,這本書的門檻還是有點高的:(。當然可以從該書的站點直接下載代碼

    3. Thread class
    package java.lang;
    public class Thread implements Runnable {
    public Thread( );
    public Thread(Runnable target);
    public Thread(ThreadGroup group, Runnable target);
    public Thread(String name);
    public Thread(ThreadGroup group, String name);
    public Thread(Runnable target, String name);
    public Thread(ThreadGroup group, Runnable target, String name);
    public Thread(ThreadGroup group, Runnable target, String name,
    long stackSize);
    public void start( );
    public void run( );
    }
    Thread name: 線程名,用以打印線程信息時用,缺省為Thread-N。
    Runnable target:可運行的目標。一個可運行的對象就是一串可由線程執行的指令。缺省就是在run()方法中編寫的內容。
    Thread group:線程組。對于大多數應用來說沒什么意義,缺省在同一組。
    Stack size:線程堆棧大小,與平臺相關。為了使程序更具可移植性,并不推薦使用。

    2.3 The Lifecycle of a Thread

     

    1. Creating a Thread:創建一個線程對象,并沒有開始執行一個線程
    2. Starting a Thread:在調用線程對象的start()方法前,一直處于等待狀態。可以通過調用isAlive()方法來獲取線程是否正在運行;
    3. Terminating a Thread:線程在以下情況下結束  
      1)執行完run()中的語句;
      2)拋出一個異常,包括其沒有捕獲的異常;
      3)其所在的程序(或者容器)調用System.exit()。
    注意:一個線程結束后,就不能再次啟動,該線程就變成一個普通的對象,如果試圖再次調用start()方法,將拋出java.lang.IllegalThreadStateException異常。如果想多次運行,要么創建新的對象;要么就是不要結束該線程。
    4. Java并沒有提供可靠的方法來暫停、掛起或者重啟一個線程的方法,線程本身能通過sleep()函數來暫停執行。目前依然只能確保若干毫秒級別的精度。
    5. Thread Cleanup:線程終止后,線程對象依然存在。可以通過join()方法以阻塞方式(blocked)來等待某個線程終止。

    2.4 Two Approches to Stop a Thread

    1. Setting a Flag:在線程的執行過程中判斷其它線程是否將標志置位。其缺點是有時間延遲,尤其是當進入了某些被阻塞的函數如:sleep(), wait()等;
    2. 調用線程的interrupt()方法,線程的執行過程中改為判斷isInterrupted()的返回值。可以解決線程進入阻塞導致的延遲,但依然解決不了超長時間計算而導致的延遲。interrupt()有兩個作用:1)終止sleep()和wait(),拋出InterruptedException;2)使isInterrupted()返回true。下面這段代碼將演示interrupt()的作用:

    public class TestInterrupted {
    public static void main(String args[]) throws InterruptedException{
    Foo foo = new Foo();
    foo.start();
    Thread.currentThread().sleep(100); //注釋掉這句有什么影響呢?
    System.out.println("before interrupt");
    foo.interrupt();
    System.out.println("after interrupt");
    } }

    class Foo extends Thread {
    public void run() {
    try{
    while(!isInterrupted()) {
    System.out.println("start calculating...");
    double pi = 3.1415926;
    for(int i=0; i<5; i++) {
    for(long j=0; j<1000000; j++) {
    pi *= pi;
    }
    System.out.println("i="+i);
    }
    System.out.println("before sleep");
    sleep(5000); //注釋掉這句及相關的try...catch語句,又怎樣呢?
    System.out.println("after sleep");
    }
    } catch(InterruptedException ex) {
    ex.printStackTrace(System.out);
    }
    }
    }

     

    2.5 The Runnable Interface

    為什么有了Thread類,還要有Runnable接口呢?最主要的原因是為了解決Java不能多重繼承的問題。線程繼承自Thread,還是僅僅實現Runnable接口,取決于這個線程的作用。不過,如果僅僅實現Runnable接口,則在線程里不能使用Thread類的方法,比如interrupt()和isInterrupted()。當然,這個還是要取決于實際情況。

    2.6 Threads and Objects

    主要講解線程與對象的關系。線程根本上還是個對象,其可以存取任何對象包括其它線程對象,當然也能被其它對象存取,只要沒有因為爭奪公共資源而被鎖定,線程對象中的任何屬性和方法都有可能同時執行。并且Java中的鎖只針對對象本身,并不包括對象下的屬性;而對方法同步,則等同于對對象同步。更進一步的說明還可以參考《Practical Java》中的"實踐46:面對instance函數,synchronized鎖定的是對象(object)而非函數(methods)或代碼(code)"。下面用兩段代碼來說明一下:
    代碼1:

    public class TestLock {
    public static void main(String args[])
    throws InterruptedException{
    Foo foo = new Foo();
    foo.start();
    Thread t = Thread.currentThread();
    for(int i=0; i<10; i++) {
    foo.setInt2("Main", i, i+20);
    }
    }
    }

    class Foo extends Thread{
    protected int arrI[] = new int[10];

    public void run() {
    try {
    for(int i=0; i<10; i++) {
    setInt("Foo", i, i);
    }
    } catch(InterruptedException ex) {}
    }

    public synchronized void setInt(String from, int pos, int val)
    throws InterruptedException{
    arrI[pos] = val;
    sleep((long)(Math.random()*5000));
    System.out.println(from+":arrI["+pos+"]="+arrI[pos]);
    }

    public void setInt2(String from, int pos, int val)
    throws InterruptedException {
    synchronized(arrI){
    arrI[pos] = val;
    sleep((long)(Math.random()*5000));
    System.out.println(from+":arrI["+pos+"]="+arrI[pos]);
    }
    }
    }
    結果:非線程安全,setInt()在對象上加鎖,而setInt2()在屬性arrI上加鎖,不同的鎖不能保證線程安全。可能的結果如下:
    Foo:arrI[0]=0 
    Main:arrI[0]=0
    Main:arrI[1]=21
    Main:arrI[2]=22
    Foo:arrI[1]=21
    Main:arrI[3]=23
    Main:arrI[4]=24
    Main:arrI[5]=25
    Foo:arrI[2]=2
    Main:arrI[6]=26
    Main:arrI[7]=27
    Foo:arrI[3]=3
    Foo:arrI[4]=4
    Main:arrI[8]=28
    Main:arrI[9]=29
    Foo:arrI[5]=5
    Foo:arrI[6]=6
    Foo:arrI[7]=7
    Foo:arrI[8]=8
    Foo:arrI[9]=9

    代碼2:
    public class TestLock1 {
    public static void main(String args[])
    throws InterruptedException{
    Foo1 foo = new Foo1();
    foo.start();
    Thread t = Thread.currentThread();
    for(int i=0; i<10; i++) {
    foo.setInt2("Main", i, i+20);
    }
    }
    }

    class Foo1 extends Thread{
    protected int arrI[] = new int[10];

    public void run() {
    try{
    for(int i=0; i<10; i++) {
    setInt("Foo", i, i);
    }
    }catch(InterruptedException ex){}
    }

    public synchronized void setInt(String from, int pos, int val)
    throws InterruptedException{
    arrI[pos] = val;
    sleep((long)(Math.random()*5000));
    System.out.println(from+":arrI["+pos+"]="+arrI[pos]);
    }

    public synchronized void setInt2(String from, int pos, int val)
    throws InterruptedException{
    arrI[pos] = val;
    sleep((long)(Math.random()*5000));
    System.out.println(from+":arrI["+pos+"]="+arrI[pos]);
      }
    }
    結果:線程安全
    posted @ 2006-03-05 23:25 Evan 閱讀(692) | 評論 (0)編輯 收藏

    Chapter 3. Data synchronization

    第二章中介紹了如何創建線程對象、啟動和終止線程。但多線程編程的關鍵在于多個線程之間數據的共享和同步,從這一章開始,將詳細介紹線程之間數據的共享和同步的各種方法。

    3.1 The Synchronized Keywor
    1. synchronized是Java中最基本也最常用的用來編寫多線程安全代碼的關鍵字,用以保護對多線程共享的數據的操作總是完整的;
    2. Atomic: 當一個操作被定義成原子操作時,意味著該操作在執行過程中不會被打斷;原子操作可以由硬件保證或者通過軟件來模擬;
    3. Mutex Lock: 在很多多線程系統,通過互斥鎖來保護的共享數據。Java中的任何一個對象都有一個與之相關的鎖,當一個方法被聲明成synchronized時,就表示當線程進入該方法前,必須獲得相應對象的鎖,在執行完畢再釋放這個鎖。從而保證同一時刻只有一個線程調用該對象上的被聲明為synchronized的方法。注意:Java的互斥鎖只能加在對象級上,獲得某個對象的鎖,并不能保證該對象的屬性和其它非synchronized的方法是線程安全的;也不能保證受保護的方法里調用的其它對象是多線程安全的,除非任何調用這些沒有被保護的方法或者對象只通過受保護的方法進行調用。所以,編寫線程安全的代碼關鍵就在于規劃方法和對象之間的調用關系,并盡量采用相同對象的鎖來進行同步控制。

    3.2 The Volatile Keyword
    1. Scope of a Lock: 鎖的作用范圍即獲得和釋放鎖之間的那段時間。
    2. Java標準雖然聲明存取一個非long和double變量的操作是原子操作,但由于不同虛擬機實現的差異,在多線程環境下每個線程可能會保留自己的工作拷貝,而導致變量的值產生沖突。為了避免這種情況的發生,可以有兩種方法:
      1) 為變量創建聲明為synchronized的setter和getter方法,然后任何調用(包括在類類部)該變量的地方都通過setter和getter方法;
      2) 采用volatile聲明,確保每次存取這些屬性時都從主內存中讀入或者寫入主內存中;
      3) volatile僅僅用于解決Java內存模式導致的問題,只能運用在對該變量只做一個單一裝載或寫入操作且該方法的其它操作并不依賴該變量的變化。如:在一個循環體中作為遞增或遞減變量時就不能使用volatile來解決線程同步的問題。
    3. volatile的使用是有限的,一般而言,僅僅將其作為強制虛擬機總是從主內存讀寫變量的一個手段,或者某些需要其參數聲明為volatile的函數。

    3.3 More on Race Conditions
    本節以打字游戲中顯示成績的例子來解釋了可能存在的race condition。關鍵要注意以下幾點:
    1. 操作系統會隨機的切換多個線程的運行,因此當多個線程調用同一個方法時,可能在任何地方被暫停而將控制權交給另外的線程;
    2. 為了減少被誤用的可能,總是假設方法有可能被多個線程調用;
    3. 鎖僅僅與某個特定實例相關,而與任何方法和類都無關,這一點當需要存取類屬性或方法時要特別注意;
    4. 任何時候只有一個線程能夠運行某個類中一個被聲明為synchronized的靜態方法;一個線程只能運行某個特定實例中一個被聲明為synchronized的非靜態方法。

    3.4 Explicit Locking
    1. 學過Win32下編寫多線程的朋友剛開始可能會被Java的Synchronized關鍵詞搞糊涂。因為Java中的任何一個對象都有一個與之相關的鎖,而不象在Win32下要先定義一個互斥量,然后再調用一個函數進入或者離開互斥區域。在JDK 1.5以后也開始提供這種顯示聲明的鎖。JDK 1.5中定義了一個Lock接口和一些類,允許程序員顯示的使用鎖對象。
    2. 在Lock接口里有兩個方法:lock()和unlock()用來獲取和釋放鎖對象,從而保證受保護的代碼區域是線程安全的。
    3. 使用鎖對象的一個好處在于可以被保存、傳遞和拋棄,以在比較復雜的多線程應用中使用統一的鎖。
    在使用鎖對象時,總是將lock()和unlock()調用包含在try/finally塊中,以防止運行時異常的拋出而導致死鎖的情況。

    3.5 Lock Scope
    利用lock()和unlock()方法,我們可以在任何地方使用它們,從一行代碼到跨越多個方法和對象,這樣就能根據程序設計需要來定義鎖的作用(scope of lock)范圍,而不是象以前局限在對象的層次上。

    3.5.1 Synchronized Blocks

    1. 利用synchronized關鍵字也能建立同步塊,而不一定非得同步整個方法。
    2. 同步一段代碼時,需要明確的指定獲取哪個對象的鎖(這就類似于鎖對象了),這樣,就可以在多個方法或對象中共享這個鎖對象。

    3.6 Choosing a Locking Mechanism
    使用鎖對象還是同步關鍵字(synchronized),這個取決于開發員自己。使用synchronized比較簡單,但相對而言,比較隱晦,在比較復雜的情況下(如要同時同步靜態和非靜態方法時),沒有鎖對象來得直觀和統一。一般而言,synchronized簡單,易于使用;但同時相對而言效率較低,且不能跨越多個方法。

    3.6.1 The Lock Interface
    public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit)
    throws InterruptedException;
    void unlock();
    Condition newCondition();
    }
    其中的tryLock()和tryLock(long, TimeUnit)將嘗試獲取鎖對象,如果不能獲取或在指定時間內不能獲取,將立即返回。調用者可以根據返回值來判斷是否獲得了鎖對象以做進一步的操作。

    3.7 Nested Locks
    1. 在一個同步方法內調用同一個對象上的另外的同步方法的情況,稱之為嵌套鎖(nested locking)。系統將自動執行嵌套的同步方法,而無須等待鎖的釋放。因此,有的時候,即使一些私有方法僅僅被已同步的方法調用,我們也給其加上synchronized關鍵字,以減少后續維護時可能產生的誤導。
    2. ReenterantLock類也支持嵌套鎖。在ReenterantLock類維持一個計數器,只有當這個計數器為0時,才會釋放鎖。注意:這個特性是ReenterantLock類的特性,而不是所有實現Lock接口的類的特性。
    3. 需要支持嵌套鎖的一個原因是方法之間交叉調用(cross-calling)。設想對象a的方法M1調用對象b的方法N1,然后N1再調用對象a的方法M2,而M1和M2都是同步方法,如果不支持嵌套鎖,則N1將在調用M2時等待M1釋放鎖,而M1則由于N1沒有返回永遠也不會釋放鎖,這樣就產生了死鎖。
    4. synchronized和Lock接口并沒有提供鎖對象被嵌套獲取的次數,但ReentrantLock則提供了這樣一種機制:
    public class ReentrantLock implements Lock {
    public int getHoldCount();
    public boolean isLocked();
    public boolean isHeldByCurrentThread();
    public int getQueueLength();
    ...
    }
    其中:
        1) getHoldCount()返回當前線程的獲取次數,返回0并不表示該鎖是可獲取的,有可能沒有被當前線程獲得;
        2) isLocked()判斷該鎖對象是否被任何線程獲得;
        3) isHeldByCurrentThread()判斷是否由當前線程獲得;
        4) getQueueLength()用來估計當前有多少線程在等待獲取這個鎖對象。

    3.8 Deadlock
    介紹了死鎖的概念,并修改例子代碼來演示deadlock的產生。死鎖一般產生在多個線程在多個鎖上同步時產生的;當然,多個線程在判斷多個條件的時候,也有可能產生死鎖。

    3.9 Lock Fairness
    1. Java里鎖的獲取機制依賴于底層多線程系統的實現,并不保證一個特定的順序;
    2. ReentrantLock類提供一個先進先出(first-in-first-out)的獲取鎖的選項(在創建鎖對象時傳入true值)。
    文章來源:http://blog.csdn.net/evanwhj/archive/2006/03/05/616068.aspx
    posted @ 2006-03-05 23:25 Evan 閱讀(504) | 評論 (0)編輯 收藏

    從2006年第五期的《讀者》上看到下面幾段言論,覺得頗有點意思,摘錄如下:
    互聯網就像是酒,看你怎么用,如果你生性孤僻,互聯網會讓你更加孤僻,如果你要和別人交流,它也會很容易讓你和別人交流。
    ——某位技術專家說
    其實,按我的說法,互聯網應該是個加速器,它往往會加劇你性格中不太容易控制的方面而變得益發不可收拾。當然,這只是對于12px的普通網友而言;而少數精英卻能從中獲得不菲的名利。

    愛情之酒甜而苦。兩人喝,是甘露;三人喝,是酸醋;隨便喝,要中毒。
    ——陶行知

    不是懷鄉,沒有鄉愁;不是近鄉,沒有情怯;不是還鄉,沒有衣錦;不是林黛玉,沒有眼淚。
    ——李敖自述訪問大陸的心情
    不管怎樣說,李大師還是很有才氣的,并且他特立獨行、有仇必報的性格——不管好壞——也是頗令人欽佩的。只是畢竟歲月不饒人,李大師越來越有點叨嘮的問題了。同樣的一些話、一些事會在一系列相關的場合連續不斷的說,不怕你耳朵不起繭。就像如果人家問他的風流趣事,他基本上都會說那個17朵玫瑰的故事一樣。然后一眾主持和現場觀眾都一定會做出欽佩至極,大呼高明狀。

    情如魚水是夫妻雙方最高的追求,但是我們都容易犯一個錯誤,即總認為自己是水,而對方是魚。
    ——BBS經典語錄
    其實,總有一個是魚,一個是水的。再說,也不一定是水的必定占上風呀。無水的魚固然活不了,但聰明的魚總會在干死前跳到另外的水中;而無魚的水,也必會少了生機,甚而至于發臭了。

    人要有三個頭腦:天生的一個頭腦,從書中得來的一個頭腦,從生活中得來的一個頭腦。
    ——契珂夫
    人往往比較容易受第一個頭腦的控制,受第三個頭腦的誘惑,第二個頭腦往往比較難以獲得,在決策時也比較難以取得主動權。

    除非你是比爾.蓋茨,否則你跟破產之間僅有一場重病之隔。
    ——美國進行的一項調查發現,美國有半數破產者是被猛漲的醫藥費所拖累,主持這項調查的一位教授如此感嘆。
    如果這不是托,那么中國的現狀似乎也不是個別的現象。但是,在中國,也許還有另外一個結局,那就是等死,即使得的不是絕癥

    posted @ 2006-03-05 23:25 Evan 閱讀(275) | 評論 (0)編輯 收藏


    播放視頻文件

    引用內容
    我在這兒等著你回來
    等著你回來
    看那桃花開
    把那花兒采
    桃花朵朵開
    暖暖的春風迎面吹
    枝頭鳥兒成雙對
    情人心花兒開
    啊喲啊喲
    你比花還美妙
    叫我忘不了
    秋又去春又來
    記得我的愛
    給你把花戴
    嘗嘗家鄉菜
    團圓樂開懷
    暖暖的春風迎面收
    別把我忘懷
    把那花兒揀
    【聽后感】對我而言,阿牛的歌,屬于還能聽一聽的范圍,至少可以讓人輕松一點,開心一下。阿牛春節前去臺灣宣傳,上吳宗憲的節目,被吳惡搞了一把,喝掉大半扎各種調料混在一起的東東,主持和其他嘉賓在前面自娛自樂去了,他一個人在旁邊默默地喝著。然后吳回過來頭大吃一驚,說:“誰讓你全喝掉的。”為表風范,吳表示要陪喝,可是連一口也喝不下去的。可見阿牛有多么老實天真了。估計,阿牛以后看到吳宗憲的背影就怕了,呵呵。



    文章來源:http://blog.csdn.net/evanwhj/archive/2006/03/05/616108.aspx
    posted @ 2006-03-05 23:25 Evan 閱讀(333) | 評論 (0)編輯 收藏

    播放視頻文件
    ?

    故事背景是很感人的網絡小說《你的肩上有蜻蜓么》
    引用內容
    有一對非常恩愛的戀人,他們每天都去海邊看日出,晚上去海邊送夕陽,可是有一天,在一場車禍中,女孩不幸受了重傷,幾天幾夜都沒有醒過來,男孩日夜守在床前,哭干了眼淚。

    一個月過去了,女孩仍然昏睡,而男孩早已憔悴不堪了,看他仍苦苦支撐。上帝被他感動了,決定給這個執著的男孩一個例外,上帝問他:“你愿意用你自己的生命作為交換嗎?”男孩毫不猶豫的回答“我愿意”上帝說:“那好吧,我可以讓你的戀人很快醒來,看你要答應我 化做三年的蜻蜓。”

    天亮了,南海變成了一只蜻蜓,他匆匆地飛到了醫院,女孩真的醒了,她還在跟身旁的一名醫生在交談。

    女孩出院了,但是她并不快樂。她四處打聽男孩的下落,女孩整天不停地尋找,早已化成蜻蜓的男孩卻無時無刻不圍繞在的身邊,只是他不會呼喊,不會擁抱,他只能默默地承受著她視而不見。夏天過去了,蜻蜓不得不離開這里,他最后一次飛落在女孩的肩上。他想用細小的嘴來親親她的額頭,然而他弱小的身體不足以被她發現。春天來了,蜻蜓迫不及待地飛回來,然而,她那熟悉的身影旁站著高大而英俊的男人,那一剎那,蜻蜓幾乎快從半空中跌落下來。蜻蜓傷心極了,他常常會看到那個男人帶著自己的戀人在海邊看日出,晚上又在海邊看日落,而他自己除了偶然能停落在的肩上之外,什么也做不了。第三年夏天,蜻蜓已不再常常去看望自己的戀人了。她的肩被男醫生擁著,臉被男醫生輕輕地吻著,根本沒時間去留意一只傷心的蜻蜓,更沒有心情去懷念過去。三年的期限很快就要到了。最后一天,蜻蜓昔日的戀人跟那個男醫生舉行了婚禮。

    蜻蜓悄悄地飛進了教堂,落在上帝的肩膀上,他聽到下面的戀人對上帝發誓說“我愿意”他看著男醫生把戒指戴到昔日戀人的手上,然后他看見他們甜蜜地親吻。蜻蜓流下了傷心的眼淚。

    上帝嘆息著:“你后悔了嗎?”蜻蜓擦干了眼淚:“沒有!”上帝又帶著一絲愉悅說:“明天你就可以變回你自己了。”蜻蜓搖搖頭說:“就讓我一輩子做蜻蜓吧……”

    有些緣分是注定要失去的,愛一個人不一定要擁有,但是擁有一個人就一定要好好去愛他。你的肩上有蜻蜓嗎?
    【聽后感】歌曲還沒有細聽,但覺得沒有那篇小說來得感人,就像電影怎么拍也拍不出原著的味道;小說比較感人,但換了我,寧愿堅持等她醒來;或者讓上帝給我另外一個選擇,那就是雙雙化成蝴蝶,所以梁山伯與祝英臺的故事比較符合中國人的口味。另外就是,愛人之間的愛一定要是雙向的;如果三年后發現女孩不愛我了,用不著后悔,但我必然會再變成人,去找我的真愛。
    posted @ 2006-03-05 23:25 Evan 閱讀(1836) | 評論 (6)編輯 收藏


    昨天晚上上了床,拿著遙控器隨便轉著臺,突然看到周星馳的《喜劇之王》,又碰巧看到飄飄(張柏芝飾)去看伊天仇(周星馳飾)自導自演精武門,然后飄飄也自說自話地上臺湊熱鬧,被伊天仇摔了個四腳朝天,然后坐在海灘邊聊天。這時,他們之間有這樣一段對話:
      飄飄:前面漆黑一片,什么也看不到。
      伊天仇:可是等到天亮,景色就會很美麗了。
    這個對話大概就是這樣子,是啊,如果你現在眼前一片漆黑,那倒真用不著怎樣害怕,天也許就快亮了。怕的就是,眼前白茫茫一片,分不清到底是快天亮了呢,還是天要黑了。

    接下來,飄飄就和伊天仇一夜風流,處于人生最低谷的伊天仇不知道這是愛情呢,還是僅僅一夜情,于是翻箱倒柜,將能找到的所有錢、手表之類的全部放在了飄飄的衣服上,繼續裝睡。而飄飄呢,也以為伊天仇僅僅是將自己當作風塵女子,也只能瀟灑地走開。然而,當伊天仇追出來說:“我養你”的時候,我覺得應該是全劇最精彩的情節之一了。兩個處于社會底層且又在自己最失意的時候,終于能夠找到相互欣賞和珍惜的愛人,的確是比較感人的。難怪飄飄在出租車里拿出伊天仇一直看的那本怎樣當喜劇演員的書時會淚流滿面了。

    唉,現在看片子,有的時候連這樣一句兩句令人欣賞的臺詞也沒有了。

    文章來源:http://blog.csdn.net/evanwhj/archive/2006/03/05/616120.aspx
    posted @ 2006-03-05 23:25 Evan 閱讀(374) | 評論 (0)編輯 收藏

    主站蜘蛛池模板: 亚洲精品国产成人影院| 性短视频在线观看免费不卡流畅| 成年人视频在线观看免费| 亚洲国产日韩在线成人蜜芽| 8x8×在线永久免费视频| 亚洲精品中文字幕麻豆| 91免费国产精品| 亚洲专区中文字幕| 成人免费视频试看120秒| 亚洲AV日韩AV无码污污网站| 凹凸精品视频分类国产品免费| 91精品国产免费| 亚洲国产成人精品久久| 色视频色露露永久免费观看| 国产精品亚洲精品久久精品| 亚洲无码精品浪潮| 国偷自产一区二区免费视频| 亚洲欧洲日本国产| 全免费a级毛片免费看无码| 十八禁的黄污污免费网站| 亚洲精品无码成人片久久| 99久久人妻精品免费一区| va天堂va亚洲va影视中文字幕| 成人毛片免费观看视频在线| 一级大黄美女免费播放| 亚洲嫩草影院久久精品| 成年女人色毛片免费看| 狠狠躁狠狠爱免费视频无码| 亚洲情a成黄在线观看动漫尤物| 国色精品卡一卡2卡3卡4卡免费| 亚洲精品无码永久在线观看男男| 亚洲国产精品日韩专区AV| 伊人久久免费视频| 亚洲AV性色在线观看| 亚洲精品无码不卡在线播HE| 国产免费女女脚奴视频网| 日韩在线视频免费| 亚洲视频一区在线播放| 免费一级毛片在线播放| 精品无码AV无码免费专区| 黄网站色视频免费看无下截 |