(友情提示:本博文章歡迎轉載,但請注明出處:陳新漢,http://m.tkk7.com/hankchen)
一、并發集合類的選擇
同步的集合類Hashtable
和Vector
,以及同步的包裝器類Collections.synchronizedMap
和Collections.synchronizedList
,為Map
和List
提供了基本的有條件的線程安全的實現。然而,某些因素使得它們并不適用于具有高度并發性的應用程序中――它們的集合范圍的單鎖特性對于可伸縮性來說是一個障礙,而且,很多時候還必須在一段較長的時間內鎖定一個集合,以防止出現ConcurrentModificationException
s異常。
ConcurrentHashMap
和CopyOnWriteArrayList
實現提供了更高的并發性,同時還保住了線程安全性,只不過在對其調用者的承諾上打了點折扣。ConcurrentHashMap
和CopyOnWriteArrayList
并不是在您使用HashMap
或ArrayList
的任何地方都一定有用,但是它們是設計用來優化某些特定的公用解決方案的。許多并發應用程序將從對它們的使用中獲得好處。
總結:在多線程并發情況下,為了避免ConcurrentModificationException
s異常,建議使用ConcurrentHashMap
和CopyOnWriteArrayList
。
還有下面的幾個可以考慮:ConcurrentLinkedQueue、CopyOnWriteArraySet、LinkedBlockingQueue、ArrayBlockingQueue
二、高效的乘除運算
服務器計算時,對于乘除運算,采用下面的方式:
A*2=a<<1
A/2=a>>1
這樣可以提高運算效率。
三、原子自增器
多線程環境下,
AtomicInteger
可用在應用程序中(如以原子方式增加的計數器),并且不能用于替換
Integer
。但是,此類確實擴展了
Number
,允許那些處理基于數字類的工具和實用工具進行統一訪問。
例如:
private AtomicInteger bomdIdCreator = new AtomicInteger(); //自增序列號
/**
*得到新的炸彈ID,保持自增
*@return
*/
public int getNewBombID(){
return bomdIdCreator.addAndGet(1);
}
四、多線程鎖機制實現
多線程環境下,為了避免資源競爭,引入了鎖機制。一般實現鎖機制有下面幾種方法:
1.
同步方法、同步塊:
synchronized
2.
監視器方法:(
wait
、
notify
和
notifyAll
)
3. ReentrantLock
注意:ReentrantLock是一個可重入的互斥鎖
Lock
,它具有與使用
synchronized
方法和語句所訪問的隱式監視器鎖相同的一些基本行為和語義,但功能更強大。
例如:建議總是立即實踐,使用
lock
塊來調用
try
,在之前
/
之后的構造中,最典型的代碼如下:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
五、線程池的實現方式
Doug Lea
編寫了一個優秀的并發實用程序開放源碼庫
util.concurrent
,它包括互斥、信號量、諸如在并發訪問下執行得很好的隊列和散列表之類集合類以及幾個工作隊列實現。該包中的
ThreadPoolExecutor
類是一種有效的、廣泛使用的以工作隊列為基礎的線程池的正確實現。您無須嘗試編寫您自己的線程池,這樣做容易出錯,相反您可以考慮使用
util.concurrent
中的一些實用程序。
線程池可以解決兩個不同問題:由于減少了每個任務調用的開銷,它們通常可以在執行大量異步任務時提供增強的性能,并且還可以提供綁定和管理資源(包括執行集合任務時使用的線程)的方法。
強烈建議程序員使用較為方便的
Executors
工廠方法
Executors.newCachedThreadPool()
(無界線程池,可以進行自動線程回收)、
Executors.newFixedThreadPool(int)
(固定大小線程池)和
Executors.newSingleThreadExecutor()
(單個后臺線程),它們均為大多數使用場景預定義了設置。
例如:
public class ThreadPoolExecutorTest {
final static ExecutorService threadPool=Executors.newCachedThreadPool(); //簡單線程池實現
/**
* @param args
*/
public static void main(String[] args) {
for(int i=0;i<10;i++){
threadPool.execute(new Runnable(){
public void run() {
System.out.println("aaa"+this.getClass().getName());
//do other things
}
});
}
}
}
六、實現定時任務的幾種方式比較
1.
使用原始的
Timer
類
2. ScheduledThreadPoolExecutor
(
JDK 1.5
新增)
3. Quatz
開源項目
從
Java 5.0
開始,
java.util.concurrent
包中增加了一個
ScheduledThreadPoolExecutor
類,用來實現定時任務和線程池的管理,比起
Timer
簡陋的實現是要強大得多。利用
ScheduledThreadPoolExecutor
的
scheduleAtFixedRate()
和
scheduleWithFixedDelay()
兩個方法就可以實現任務調度的基本功能,從前用
Timer
實現的功能應該要遷移到
scheduleWithFixedDelay()
上了。
注意:
ScheduledThreadPoolExecutor
是實現
ScheduledExecutorService
接口的具體類。
1)public static final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(2);
2)public static final ScheduledThreadPoolExecutor scheduledExecutor=new ScheduledThreadPoolExecutor(2);
這兩種方式是一樣的,都是得到一個可調度的線程池。
ScheduledThreadPoolExecutor
與
Timer
的區別:
1.
Timer
的主要方法有:
//
安排在指定的時間執行
void schedule(TimerTask task, Date time)
//
安排在指定的時間開始以
重復的延時
執行
void schedule(TimerTask task, Date firstTime, long period)
//
安排在指定的延遲后執行
void schedule(TimerTask task, long delay)
//
安排在指定的延遲后開始以重復的延時執行
void schedule(TimerTask task, long delay, long period)
//
安排在指定的時間開始以
重復的速率
執行
void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
//
安排在指定的延遲后開始以重復的速率執行
void scheduleAtFixedRate(TimerTask task, long delay, long period)
注:
重復的延時
和
重復的速率
的區別在于,前者是在前一個任務的執行結束后間隔
period
時間再開始下一次執行;而
scheduleAtFixedRate
則是會盡量按照任務的初始時間來按照間隔
period
時間執行。如果一次任務執行由于某些原因被延遲了,用
schedule()
調度的后續任務同樣也會被延遲,而用
scheduleAtFixedRate()
則會快速的開始兩次或者多次執行,是后續任務的執行時間能夠趕上來。
2.
ScheduledThreadPoolExecutor
的主要方法:
//
在指定的延遲后執行
<V>ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)
//
在指定的延遲后執行
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
//
在指定的延遲后以固定速率執行
(
類似
Timer.scheduleAtFixedRate())
ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
//
在指定的延遲后以固定間隔執行
(
類似
Timer.schedule())
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
比較:
(
1
)
Timer
對調度的支持是基于絕對時間的,因此任務對系統時間的改變是敏感的;而
ScheduledThreadPoolExecutor
支持相對時間。
(
2
)
Timer
使用單線程方式來執行所有的
TimerTask
,如果某個
TimerTask
很耗時則會影響到其他
TimerTask
的執行;而
ScheduledThreadPoolExecutor
則可以構造一個固定大小的線程池來執行任務。
(
3
)
Timer
不會捕獲由
TimerTask
拋出的未檢查異常,故當有異常拋出時,
Timer
會終止,導致未執行完的
TimerTask
不再執行,新的
TimerTask
也不能被調度;
ScheduledThreadPoolExecutor
對這個問題進行了妥善的處理,不會影響其他任務的執行。
結論:
Timer
有這么多的缺點,如果是使用
JDK1.5
以上的話,應該沒什么理由要使用
Timer
來進行調度。
(友情提示:本博文章歡迎轉載,但請注明出處:陳新漢,http://m.tkk7.com/hankchen)