另外,采用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,嗯,這下所有的中文注釋都乖乖出來了。
2006年3月6日 #
另外,采用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,嗯,這下所有的中文注釋都乖乖出來了。
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)
...
不要憂慮“不要為明天憂慮,天上的飛鳥,不耕種也不收獲,上天尚且要養活它,田野里的百合花,從不憂慮它能不能開花,是不是可以開得和其它一樣美,但是它就自然的開花了,開得比所羅門皇冠上的珍珠還美。你呢,憂慮什么呢? 人比飛鳥和百合花貴重多了,上帝會棄你不顧嗎?” 閱讀全文
“我雖然行過死蔭的幽谷,
也不怕遭害;
因為你與我同在;
你的杖,你的竿,都安慰我。”
--《詩篇第二十三篇第四節》
當然,一開始我不知道這是《舊約全書·詩篇》中的內容,于是就Google了一把,找到下面這篇文章《因為你與我同在》:
“
我去一個鄉村教會講道,在接待的弟兄家休息。看見弟兄的兒子學習很用功,很是喜愛。我請小朋友將他的語文課本借給我看。這是人教版小學四年級語文課本。當中有一篇文章吸引了我——
1989 年美國洛杉磯大地震,30萬人在不到4分鐘時間里受到不同程度的傷害。這其間一個小學的慘況讓人心酸。多少孩子的父母因在地震中痛失愛子而哀聲聞天,面對 地震后的學校廢墟只能絕望離去。但一個父親卻在廢墟中不斷地挖掘尋找自己那才七歲的叫阿曼達的兒子。救火隊長擋住他,“太危險了,隨時可能發生大爆炸,請 你離開。”別的學生家長也勸說,“太晚了,沒有希望了。”可這位父親不顧這些勸阻,在廢墟中不斷地尋找。就在他挖了整整八個小時后,父親聽見瓦礫堆底下傳 出他十分熟悉的聲音,“爸爸,是你嗎?”這時他發現有14個孩子在當中,因為這是在教室墻角的位置,當房頂塌下時架成大三角形,孩子們才沒被砸著。“我告 訴同學們不要害怕,說只要我爸爸活著就一定會來救我,也能救大家。因為你說過,不論發生什么,你總會和我在一起!”孩子對爸爸說。“爸爸,先讓我的同學出 去吧,我知道你會跟我在一起,我不怕。不論發生了什么,我知道你總會跟我在一起。”
14個孩子獲救了!
我們不禁會問,如果因為大爆炸的危險而絕望地放棄,如果這個父親和其他的家長一樣絕望地離開,如果阿曼達沒有“因為你與我同在”的信念,那結果又將如何?
我想說的是,絕望讓生命失去,希望使生命存留。
詩人說,“因為你與我同在。”(詩篇23:3)這樣的信心使詩人在人生的黑夜里依然有生命的曙光,在人生的冬天里可憑信宣告說,冬天來了,上帝的春天也不再遙遠。
“因為我與你同在,要拯救你。這是耶和華說的。”(耶利米書1:8)
阿曼達對父親單純的信念應一如我們對天上的父親執著的信仰。
是啊,“絕望讓生命失去,希望使生命存留”,不過呢,對于我這個不信教的人來說,大可抱著象故事中的小阿曼達相信他父親只要活著就一定會來救他的希望一樣,也不一定非要抱著耶和華會來救我們的希望的。不過,不管信什么教,基督也好,佛陀也好,其實所有經典中的智慧都是值得學習的;當然,如果你在這個浮躁的物質社會中實在找不到可以依賴的東西時,信個把教也未嘗不可。
public void actionPerformed(ActionEvent evt) {
displayCanvas.setDone(false);
producer.setDone(false);
startButton.setEnabled(false);
stopButton.setEnabled(true);
feedbackCanvas.setEnabled(true);
feedbackCanvas.requestFocus();
score.resetScore();
}
});
然后又運行了第3章的例子,發現基本上沒有這個問題。難道第3章的代碼是正確的?打開源代碼一看,重置成績的方法還是放在最后,那這里為什么又是正確的呢?我想,大約是第3章的例子中,每次點擊start按鈕,都重新創建一個線程對象的原因吧。由于創建對象和初始化線程需要一定的時間,剛好給了主線程重置成績的機會。
不知道作者有意為之呢,還是疏忽,不過,這樣的錯誤不能算是race condition的例子。
《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(long timeout): 使當前線程處于等待狀態,直到其它線程調用了nofity()或者notifyAll()方法,或者超過了指定的時間(單位為ms)為止
void wait(long timeout, int nanos):與wait(long)一樣,只是在某些JVM中可以精確到奈秒。
void notify(): 喚醒一個正在等待該對象的線程。
void notifyAll(): 喚醒所有正在等待該對象的線程。
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. 這一節中舉了一個例子用來解釋可能存在當條件被不滿足時也有可能被喚醒的情況:
2) T1檢測狀態變量,發現其不滿足條件;
3) T1調用wait(),并釋放鎖;
4) 線程T2調用另外一個同步方法,獲得鎖;
5) 線程T3調用另外一個同步方法,由于T2獲得了鎖,所以處于等待狀態;
6) T2修改狀態變量,使其滿足條件,并調用notify()方法;
7) T3獲得鎖,然后處理數據,并將狀態變量又設置為不滿足條件的狀態;
8) T3處理完畢返回;
9) T1被喚醒,但實際上此時條件并不滿足。
synchronized(obj) { while(<condition does not hold>) wait();
... // Perform action appropriate to condition }
synchronized(obj) { while(<condition does not hold>) { try { wait(); } catch (InterruptedException e) {} }
... // Perform action appropriate to condition }
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();
}
}
}
5. 除了用lock和await-and-signal來代替synchronized和wait-and-notify外,其語義和機制基本一樣。await()在進入前也會自動釋放鎖,然后再返回前重新獲得鎖;
6. 使用Condition Variables的原因:
1) 如果使用Lock對象,則必須使用condition variables;
2) 每個Lock對象可以創建多個condition variable.