不過,問題改好之後,發現更大的問題:速度超慢。更離譜的是,使用單執行緒的方式執行,竟然還比有快取的多執行緒池快10倍以上。昨天晚上看著 Profiler的分析到凌晨4點,仍然找不出問題在哪,只看到執行緒wait的頻率很高。白天上課一直恍神,完全無法理解老師在上什麼,不過可能也因為恍神太多,讓我在走回宿舍的路上精神飽滿,聽著Claudio Abbado指揮Lucerne Festival Orchestra演出的Mahler交響曲第二號,靈機一動想到了一個可能性:「會不會有人拿著互斥鎖,做一些很耗時間的工作?」回到宿舍看code,果如所料!
試比較下列這兩段程式碼,你比較喜歡哪一種寫法?
Code 1.
synchronized(this){Code 2.
if(queue.isEmpty()){
if(connectionCount.get()< maxConnectionCount){
conn = new
DBConnection(this,getRawConnection());
connectionCount.incrementAndGet();
}
}
else{...}
}
synchronized(this){一般而言,多數人都會選Code 1.,因為兩段程式碼功能一樣,Code1卻顯得比較簡潔直觀。然而,Code1就是我一開始寫的,很花時間的程式碼。
if(queue.isEmpty()){
if(connectionCount.get()< maxConnectionCount){
newConn = true;
connectionCount.incrementAndGet();
}
}
else{...}
}
if(newConn)
conn = new DBConnection(this,getRawConnection());
Code2和Code1最大的差別在於Code 2把 conn = new DBConnection(this,getRawConnection()); 從同步區塊取出。這麼做會讓效能增加數倍的原因是getRawConnection()操作太花時間。如果放在同步區塊,代表一次只有1個執行緒能夠呼叫這個方法。這個方法一次得花10秒鐘的時間去和遠端資料庫連繫,如果一次只有1個執行緒呼叫,則20個執行緒至少得花200秒。Code2的getRawConnection()呼叫因為是在同步區塊之外,能夠讓多個執行緒同時取用,就沒有這種問題。
這次的教訓告訴我們,可以放在同步區塊的程式碼越少越好。至少,非必要的話,不要把怪獸放進去。
沒有留言:
張貼留言