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