標籤:
1、使用new Thread(runnableObj)方式,而非繼承自Thread。
對於耗時的任務,應放到線程中執行
調用new Thread(runnable).start()方法啟動線程,將會線上程中調用對應的runnalbe.run方法
2、中斷線程的方式:調用interrupt方式,會置位線程中斷狀態。檢查這個中斷位可判斷線程是否被中斷:Thread.currentThread().isInterrupted().
currentThread可用於擷取當前線程。
sleep方法會清除中斷狀態並拋出InterruptedException。
interrupt()會向線程發插斷要求,並將中斷狀態置為true。
interrupted()方法會檢測是否線程被中斷了,如果是會清除中斷狀態。
線程狀態:New,Runnable可運行(調用start後的狀態,正在執行的線程狀態以及等待執行的都是可運行),Blocked被阻塞,Waitting,Timed waiting計時等待,Terminated。
被阻塞的情況:1、等待鎖被釋放;2、當線程等待另一個線程通知調度器一個條件時,它自己進入等待狀態;方法帶有逾時參數,逾時時會進入計時等待狀態。如Thread.sleep,Object.wait(),Thread.join(),Lock.tryLock(),Condition.await()
3、join()等待終止指定的線程,join(long mills)等待指定線程死亡或經過指定的毫秒數;
Thread.State getState()擷取線程的狀態
4、線程繼承自父線程的優先順序。用setProperty調整優先順序,取值0~10,值越大優先順序越高。
static void yield()此方法導致當前執行線程處於讓步狀態,有同優先順序線程存在時,會優先執行。
5、this.setDaemon(true)使線程轉換為守護線程,此方法必須線上程啟動前調用。守護線程的用途是為其他線程提供服務。當只剩下守護線程時,虛擬機器退出。守護線程應該永遠不要去訪問固定資源,如檔案、資料庫,因為它在任何時候都可能在一個執行過程中發生中斷。
6、線程的run不能拋出被檢測的異常。未被檢測的異常會導致線程終止,線程死亡前異常會被傳遞到一個用於未捕獲異常的處理器。該處理器實現了Thread.UncaughtExcetptionHandler介面。沒安裝預設處理器的話,預設處理器為空白。不為獨立的線程安裝處理器,其處理器就是ThreadGroup對象
不要在Catch語句中處理可被傳遞的異常。
javap -c -v Bank 用於對Bank.class進行反編譯
7、採用synchronized保證代碼塊的並發訪問。
ReentrantLock是一個鎖對象,可用於控制並發。在一個公用存取碼中聲明一個鎖對象。在try之前獲得鎖,finally裡釋放鎖。而非將鎖對象作為線程的成員。
鎖是可重新進入的,線程可以重複擷取已持有的鎖。鎖保持一個持有計數,在臨界區內調用其他方法時,計數加1,所調用的方法退出時,鎖減1.
留心臨界區的異常處理,不要因異常而導致跳出臨界區。
8、條件對象用於線程等待某個條件滿足時執行操作。單純的鎖無法滿足這種情境。
一個鎖可以關聯多個條件變數Condition。bankLock.newCondition()建立鎖關聯的條件變數。在臨界區內,while(condition no statisfy)condition.await();在迴圈體中調用是必須的。 通過調用條件變數中的await方法會進入該條件的等待集,阻塞,等待另一個線程調用同一條件上的signalAll方法(會解除所有等待這一條件的線程的阻塞,使得被解除阻塞的線程可以在當前線程退出阻塞之後,通過競爭實現對象的訪問)。signal()方法則是隨機解除一個阻塞的線程,這個方式需保證被解除阻塞的線程可以執行,否則會造成死結。
每個條件對象管理那些已經進入了被保護的程式碼片段,但還不能啟動並執行線程。
每個對象內都有一把鎖,要調用synchronized聲明的對象成員方法,必須事先獲得內部的對象鎖。
內部的對象鎖只有一個關聯的條件對象,wait會將線程添加到等待集中,notify/notifyAll會解除等待線程的阻塞狀態。只能在synchronized內部使用wait和notify方法實現條件對象等待某個條件實現的功能。模式與await和signal相似。
一般不要用synchronized和Condition,優先用synchronized。
線程有本機快取和寄存器,可以暫存記憶體中的值。如果使用鎖來執行並發訪問共用變數,編譯器被要求在必要的時候重新整理本機快取(更新共用變數在本地的值)來保持鎖的效應。
volatile為同步訪問提供了免鎖機制,但是不保證原子性。如果共用變數除了賦值外,不完成其他動作,可以將其聲明為volatile。
atomic對象可以原子方式對變數做加減。
9、public static final ThreadLocal<SimpleDateFormat> dataFormat=
new ThreadLocal<SimpleDateFormat>(){
protect SimpleDateFormat initialValue(){
return new SimpleDateFormat("yyyy-MM-dd");
}
}
10、鎖逾時: if(myLock.tryLock(100,TimeUnit.MILLISECONDS)){...} 逾時時拋出InterruptedException,可用於打破死結。
myCondition.await(100,TimeUnit.MILLISECONDS)
11、讀線程多寫線程少用ReentrantReadWriteLock。
private rwLock = new ReentrantReadWriteLock();
rLock=rwLock.readLock();該所被所有讀線程共用,會排斥寫操作在讀之前用rLock.lock();結束後用rLock.unlock();
wLock=rwLock.writeLock();排斥其他的讀操作和寫操作n。在寫之前用wLock.lock();結束後用wLock.unlock();
12、阻塞隊列BlockingQueue(這個有幾種繼承類:LinkedBlockingQueue, LinkedBlockingDeque, ArrayBlockingQueue)就是一個生產者消費者模型中的用於存放共用資料的隊列。該隊列有考慮生產者消費者間的並行作業,處理生產者消費者速度的Server Load Balancer。用offer添加一個元素、poll移出並返回隊頭、peek返回隊頭元素(這三種操作可指定操作的逾時時間)。
PriorityBlockingQueue優先隊列。
13、安全執行緒集合:阻塞隊列、ConcurrentHashMap, ConcurrentSkipListMap, ConcurrentSkipListSet(這兩個是安全執行緒的有序集,需事先Comparable介面), ConcurrentLinkedQueue. 這些集合的size()內部需要通過遍曆來確定集合大小,這與一般的集合不同。
這些集合返回的是弱一致性的迭代器,不一定能返回被修改後的值,但不會將同一個值返回兩次,不會拋出ConcurrentModificationException。
ConcurrentHashMap<E>(int initialCapacity, float loadFactor, int concurrentLevel);中參數是指定集合的初始容量,預設為16.concurrentLevel是並發寫者線程的估計數目,表明可供多達16個寫進程同時寫,多了會阻塞剩下的線程。
ConcurrentHashMap.putIfAbsent(k,v)是在該鍵不存在時插入kv對,否則返回鍵k對應的值。
寫數組的拷貝:
CopyOnWriteArrayList和CopyOnWriteArraySet。寫線程對底層資料做修改。如果讀線程數大於寫線程數,則這類集合很適合。讀寫可不用同步就可保持一致性。
任何集合類通過同步封裝器可變為安全執行緒的,通過給集合加入鎖來實現的,這不推薦使用,因為還是需要在訪問的代碼中加入synchronized(synchashMap){...},推薦使用ConcurrentXXX集合:
List<E> syncArrayList = Collections.synchronizedList(new ArrayList<E>());
HashMap<K,V> syncHashMap = Collections.synchronizedMap(new HashMap<K,V>());
對於經常被修改的數組列表,採用synchronized(arrayList){}會比CopyOnWriteArrayList效能更好。
14、回調Callable和Future。 Runnalbe是無返回值無參數的非同步方法呼叫,Callable和Future有返回值。Callable<E>{ E call();}只有一個方法返回值類型為E的call().
Future儲存了非同步計算結果,其get(long timeout,TimeUnit)可指定逾時時間,逾時時中斷。如果Future還在執行,則isDone()返回false。cancle()方法可取消該運算。
FutureTask封裝器,可將Callable轉化為Future何Runnable。
Callable<Integer> myCompution=..;
FutureTask<Integer> task= new FutureTask<Integer>(myCompution);//構造一個既是Future又是Runnable的對象。
Thread t= new Thread(task);
t.start();
...
Integer result=task.get();//對get調用會阻塞直到有可獲得的結果或者逾時為止。
15、執行器Executor
如果程式中有大量生命期很短的線程,最好使用線程池。線程池中包含了許多準備啟動並執行空閑線程。將Runnable交給線程池,就會有一個線程調用Runnable.run()。run退出時線程又回到線程池等待下一次運行。
使用線程池可減少並發線程數。
Executor中有許多靜態方法可用於構建線程池:newCachedThreadPool空閑線程被儲存60s;newFixedThreadPool固定線程數且空閑線程一直儲存,如果任務數大於線程數,未得到執行的任務會放到隊列中等待空閑線程可用;newSingleThreadPool只有一個線程,用於順序執行。這些方法返回實現了ExecutorService介面的ThreadPoolExecutor對象。
可用ExecutorService介面中的Future<?> submit(Runnable,T result);Future<?>submit(Callable<T> Task)方法提交Runnable和Callable對象給ExecutorService。
線程池用完後調用shutdown()關閉線程池。
步驟:
a、Executors.newFixedThreadPool()
b、submit提交Runnable或Callable對象;
c、擷取Future對象result,並調用result.get();
d、關閉線程池shutdown()
15、預定執行(定時任務)
ScheduledExecutorService介面中有為預定執行或重複執行的任務設計的方法。
ScheduledExecutorService ss=Executors.newScheduledThreadPool();
schedule(Callable/Runnable,initDelayTime,TimeUnit);
scheduleAtFixedRate(Callable/Runnable,initDelayTime,long period,TimeUnit);//每隔period執行一次
16、控制工作群組
Executor可用於控制一組相關任務。
invokeAny:多個任務執行,只要有一個執行完成就可以結束了。
invokeAll:多個任務執行,所有任務都執行完成才可以結束。使用方式如下:
List<Callable<T>> tasks=...;
List<Future<T>> results=executor.invokeAll(tasks);
for(Future<T>res:results){//順序遍曆在第一個任務耗時很長的情況下會花很多時間進行等待,推薦用ExecutorCompletionService按結果產生順序進行儲存,更有效
processFuturer(result.get());
}
17、fork-join架構(大任務劃分為小任務,並存執行後merge)
RecursiveTask用於有計算結果返回,RecursiveAction用於無計算結果返回。二者的compute用於產生並調用子任務併合並結果。
class Counter extends RecursiveTask<Integer>{//分治演算法
protect Integer compute(){
if(to-from<THRESHOLD){直接執行(單任務)}
else{
int mid=(low+high)>>1;
Counter first=new Counter(from,mid);//拆分子任務
Counter second=new Counter(mid+1,high);
invokeAll(first,second);//調度所有子任務
return first.join()+second.join();//合并結果
}
}
}
main(){
ForkJoinPool pool=new ForkJoinPool();
pool.invoke(counter);
System.out.println(counter.join());
}
18、同步器
提供了實現線程間相互合作的類:
CyclicBarrier:等待一定數目的線程都到達一個公用屏障,在執行一個處理屏障的動作。
CyclicBarrier barrier=new CyclicBarrier(numThreads,barrierAction);//當numThreads個線程都到達屏障時執行barrierAction
Runnable{
public void run(){
doSomething...
barrier.await();//等待直到屏障開啟,可設定逾時時間,逾時時拋異常,會導致其他等待await的線程都拋BrokenBarrierException異常。
...
}
}
CountDownLatch:允許等待直到計數器為0。用於當一個或多個線程需等到指定數目事件發生時。這是一次性的,只可用一次。
Exchanger:允許兩個線程在要交換對象準備好時交換對象。兩個線程在同一個資料結構的兩個不同執行個體上,一個向執行個體添加資料,另一個用於清除資料
Semaphore:允許線程等待直到被允許執行為止。用於限制訪問資源的線程數。
訊號量管理了許多permits,用於限制通過的線程數量。訊號量僅維護一個計數。其他線程通過調用release()釋放許可permits。
SynchronousQueue:允許一個線程把對象交給另一個線程。在沒顯式同步時,兩個線程將一個對象從一個線程傳到另一個線程。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
Java多線程筆記