聲明
歡迎轉載,但請保留文章原始出處:)
博客園:http://www.cnblogs.com
農民伯伯: http://over140.cnblogs.com
系列
正文
1. 先把焦點放在設計、數據結構和算法身上
備注:良好的設計、明智的選擇數據結構和算法可能比高效代碼更重要。
2. 不要依賴編譯器優化技術
3. 理解運行時(runtime)代碼優化
備注:JIT將bytecode於運行時轉換為本地二進制碼,從而提高性能。因此編譯后代碼被執行次數越多,本機代碼生成代價就很合算。
4. 連接字符串使用StringBuffer要比String快,尤其是大量字符串拼接
5. 將對象創建成本降至最小
備注:復用既有對象,不要創建非必要的對象,只在需要的時候才創建它們。
6. 將同步化(synchronization)降至最低
備注:如果synchronized函數拋出異常,則在異常離開函數之前,鎖會自動釋放。如果整個函數都需要被同步化,為了產生體積較小且執行速度較快的代碼,請優先使用函數修飾符,而不是在函數內使用synchronized代碼塊。
7. 盡可能使用stack變量
備注:如果在函數中頻繁訪問成員變量、靜態變量,可以用本地(local)變量替代,最后操作完后再賦值給成員/靜態變量。
8. 盡可能的使用static、final和private函數
備注:此類函數可以在編譯期間被靜態決議(statically resolved),而不需要動態議決(dynamic resolved)。(子類無法覆寫)
9. 類的成員變量、靜態變量都有缺省值,務須重復初始化
備注:記住,本地變量沒有缺省值(例如函數內定義的變量)。
10. 盡可能的使用基本數據類型
備注:如int、short、char、boolean,使得代碼更快更小。
11. 不要使用枚舉器(Enumeration)和迭代器(Iterator)來遍歷Vector
備注:使用for循環+get()
12. 使用System.arraycopy()來復制數組
備注:使用System.arraycopy()代替for循環,可以產生更快的代碼。如:
int size = src.length;
System.arraycopy(src, 0, dest, 0, size);
}
System.arraycopy()是以native method實現的,可以直接、高效的移動原始數組到目標數組,因此它執行速度更快。
13. 優先使用數組,然后才考慮Vector和ArrayList,理由:
a). Vector的get()是同步的
b). ArrayList基本上就是一個非線程同步的Vector,比Vector要快
c). ArrayList和Vector添加元素或移除元素都需要重新整理數組。
備注:不要僅僅因為手上有個數不定的數據需要存儲,就毫無選擇的使用Vector或ArrayList。可以考慮創建一個足夠大的數組,通常這樣可能會浪費內存,但性能上的收益可能超過內存方面的代價。
14. 手工優化代碼
a). 剔除空白函數和無用代碼
備注:以更高效的操作替換成本昂貴的操作。一個常見的優化手法是使用復式復制操作符(如+=、-=)。
c). 合並常量
備注:將變量聲明為final,使得操作在編譯器就進行。
d). 刪減相同的子表達式
備注:可用一個臨時變量代替重復的表達式。
e). 展開循環
備注:如循環次數少且已知循環次數,可展開去掉循環結構,直接訪問數組元素。缺點是會產生更多代碼。
f). 簡化代數
備注:使用數學技巧來簡化表達式。(例如從1+..+100的問題)
g). 搬移循環內的不變式
備注:循環內不變化的表達式可用移至循環外,不必重復計算表達式。
15. 編譯為本機代碼
備注:將程序的某部分編譯為本機二進制代碼,然后通過JNI訪問。
二、多線程
1. 對於實例(instance)函數,同步機制鎖定的是對象,而不是函數和代碼塊。
備注:函數或代碼塊被聲明為synchronized並非意味它在同一時刻只能有一個線程執行(同一對象不同線程調用會阻塞)。Java語言不允許將構造函數聲明為synchronized。
2. 同步實例函數和同步靜態函數爭取的是不同的locks。
備注:兩者均非多線程安全,可以使用實例變量進行同步控制,如(byte[] lock = new byte[0]),比其他任何對象都經濟。
3. 對於synchronized函數中可被修改的數據,應使之成為private,並根據需要提供訪問函數。如果訪問函數返回的是可變對象,那么可以先克隆該對象。
4. 避免無謂的同步控制
備注:過度的同步控制可能導致代碼死鎖或執行緩慢。再次提醒,當一個函數聲明為synchronized,所獲得的lock是隸屬於調用此函數的那個對象。
5. 訪問共享變量時請使用synchronized或volatile
備注:如果並發性很重要,而且不需要更新很多變量,則可以考慮使用volatile。一旦變量被聲明為volatile,在每次訪問它們時,它們就與主內存進行一致化。如果使用synchronized,只在取得lock和釋放lock時候才一致化。
6. 在單一操作(single operation)中鎖定所有用到的對象
備注:如果某個同步函數調用了某個非同步實例函數來修改對象,它是線程安全的。使用同步控制時,一定要對關鍵字synchronized所作所為牢記在心。它鎖定的是對象而非函數或代碼。
7. 以固定而全局性的順序取得多個locks(機制)以避免死鎖。P/181~P/185
備注:嵌入[鎖定順序]需要額外的一些工作、內存和執行時間。
8. 優先使用notifyAll()而非notify()
備注:notify()和notifyAll()用以喚醒處以等待狀態的線程,waite()則讓線程進入等待狀態。notify()僅僅喚醒一個線程。
9. 針對wait()和notifyAll()使用旋轉鎖(spin locks)
備注:旋轉鎖模式(spin-lock pattern)簡潔、廉價,而且能確保等待着某個條件變量的代碼能循規蹈矩。
10. 使用wait()和notifyAll()替代輪詢(polling loops)
備注:調用wait()時會釋放同步對象鎖,暫停(虛懸,suspend)此線程。被暫停的線程不會占用CPU時間,直到被喚醒。如:
{
int data;
while( true){
synchronized (pipe) {
while((data = pipe.getDate()) == 0){
try{
pipe.waite();
}
catch(InterruptedException e){}
}
}
// Process Data
}
}
11. 不要對已鎖定對象的對象引用重新賦值。
12. 不要調用stop()和suspend()
備注:stop()中止一個線程時,會釋放線程持有的所有locks,有攪亂內部數據的風險;suspend()暫時懸掛起一個線程,但不會釋放持有的locks,可能帶來死鎖的風險。兩種都會引發不可預測的行為和不正確的行為。
當線程的run()結束時,線程就中止了運行。可以用輪詢+變量來控制,如下代碼:
public void stopThread()
{
stop = true;
}
public void run()
{
while(!stop){
// Process Data
}
}
注意:這里使用了關鍵字volatile,由於Java允許線程在其 私有專用內存 中保留主內存變量的副本(可以優化),線程1對線程2調用了stopThread(),但線程2可能不會及時察覺到stop主內存變量已變化,導致不能及時中止線程。
三、類與接口
1. 實現一個final類(immutable class 不可變類)時,請遵循下列規則:
a). 聲明所有數據為private
b). 只提供取值函數(getter),不提供賦值函數(setter)
c). 在構造函數中設置有實例數據
d). 如果函數返回、接受引用final對象,請克隆這個對象。
e). 區別淺層拷貝和深層拷貝應用場景。如拷貝Vector需要使用深層拷貝。
2. 實現clone()時記得調用super.clone()
備注:不管是淺層拷貝還是深層拷貝都需要調用super.clone()。
3. 別只依賴finalize()清理內存以外的資源
備注:finalize()函數只有在垃圾回收器釋放對象占用的空間之前才會被調用,回收時可能並非所有符合回收條件的對象都被回收,也無法保證是否被調用、何時調用。實現finalize()方法時記得調用super.finalize()。
4. 在構造函數內應避免調用非final函數,以免被覆寫而改變初衷。
結束
書是從朋友那邊借過來的,拿到手也有一段時間,磨磨唧唧好多天才看了幾十頁,而余下部分從上篇文章到這篇文章也不過才3-5天。發現以這種方式來看書也不錯,一方面能加快速度,一方面由於要寫文章更加認真細讀,還能提煉把書讀薄記錄分享出來,實在是很適合我這樣的 :)