Java多執行緒筆記總結
1.執行緒的三種建立方式
對比三種方式:
- 通過繼承Thread類實現
- 通過實現Runnable介面
- 實現Callable介面
第1種方式無法繼承其他類,第2,3種可以繼承其他類;
第2,3種方式多執行緒可以共享同一個target物件,多個執行緒處理同一個資源;
一般使用第2,3種方式建立執行緒。
2.執行緒的生命週期
1.新建(new) 2.就緒(start) 3.執行(獲得cpu資源) 4.阻塞(sleep,IO阻塞等)4.死亡(執行完成,Exception/Error)
3.執行緒常用方法
join() :Thread物件呼叫,執行緒A呼叫join(),其他執行緒被阻塞,直到執行緒A執行完為止。
setDaemon(true) : Thread物件呼叫,設定成後臺執行緒; 當所有前臺執行緒都死亡,後臺執行緒自動死亡。
- sleep(): 靜態方法,讓正在執行的執行緒暫停,進入阻塞狀態。
- yield(): 靜態方法,讓正在進行的執行緒暫停,進入就緒狀態,系統的執行緒排程器重新排程一次;只有優先順序比當前呼叫yield()的執行緒高或相同,並且處於就緒狀態的執行緒才能獲得執行。
- setPriority(int newPriority), getPriority(): 設定和獲取執行緒的優先順序;newPriority範圍1-10;Thread三個靜態常量,MAX_PRIORITY, 10; NORM_PRIORITY, 5; MIN_PRIORITY, 1;子執行緒和父執行緒具有相同的優先順序,優先順序高的執行緒比優先順序低的執行緒執行機會更多。
4.執行緒同步
多個執行緒訪問同一個資料時(執行緒排程的不確定性),很容易出現執行緒安全問題。解決辦法:引入同步監視器,任意時刻只能有一個執行緒獲得對同步監視器的鎖定,當同步程式碼塊執行結束後,該執行緒釋放對該同步監視器的鎖定。
“加鎖”—>”修改共享資源”->”釋放鎖”
同步程式碼塊
// obj就是同步監視器
synchronized(obj){
同步程式碼塊
}
同步方法
使用synchronized修飾某個方法,則該方法稱為同步方法,同步方法的同步監視器是this;
只需對會改變競爭資源的方法進行同步。
任何執行緒在進入同步程式碼塊或同步方法前,必須獲得同步監視器的鎖定。
釋放同步監視器的鎖定的時機:
- 當前執行緒的同步程式碼塊,同步方法執行結束
- 當前執行緒在同步程式碼塊,同步方法中遇到break,return終止了執行
- 當前執行緒在同步程式碼塊,同步方法中出現了未處理的Error或Exception
- 執行了同步監視器物件的wait()方法
同步鎖(Lock)
Lock對共享資源的獨佔訪問,每次只能一個執行緒對Lock物件加鎖。
Lock,ReadWriteLock介面,對應的實現類ReentrantLock(可重入鎖),ReentrantReadWriteLock。
class A{
private final ReentrantLock lock = ReentrantLock();
public void method(){
// 加鎖
lock.lock();
try{
// 修改共享資源
}finally{
// 釋放鎖
lock.unlock();
}
}
}
死鎖
兩個執行緒互相等待對方釋放同步監視器,就發生了死鎖。
執行緒池
在系統啟動時,建立大量空閒的執行緒,將一個Runnable物件或Callable物件傳給執行緒池,執行緒池會啟動一個執行緒執行對應的run()或call(),當run()或call()執行完成後,該執行緒返回到執行緒池成為空閒狀態,等待下個Runnable物件或Callable物件。
建立執行緒池
Executors工廠類,提供如下靜態方法建立不同的執行緒池:
// 具有快取功能的執行緒池,系統根據需要建立執行緒,這些執行緒會被快取線上程池中
newCachedThreadPool()
// 固定數量,可重用的執行緒池
newFixedThreadPool(int nThreads)
// 單執行緒的執行緒池
newSingleThreadExecutor()
// 指定執行緒數量,並可指定延遲時間才執行執行緒任務
newScheduleThreadPool(int corePoolSize)
// 單執行緒,並可指定延遲時間才執行執行緒任務
newSingleThreadScheduleExecutor()
前三個靜態方法return ExecutorService
後兩個靜態方法return ScheduledExecutorService
ExecutorService代表執行緒池,提供3個方法:
// 將一個Runnable物件提交給執行緒池,Future返回null
Future<?> submit(Runnable task)
// 將一個Runnable物件提交給執行緒池,Future返回指定結果result
Future<T> submit(Runnable task, T result)
// 將一個Callable物件提交給執行緒池,Future返回Callable物件中call()方法
Future<T> submit(Callable<T> task)
步驟:
- Executors靜態工廠類建立ExecutorService
- 建立Runnable或Callable物件,作為執行緒執行體
- 呼叫ExecutorService例項的submit()方法提交Runnable或Callable
- 執行緒池關閉,呼叫ExecutorService例項的shutdown()方法
ThreadLocal
執行緒區域性變數,把資料放在ThreadLocal中,就可以為每個執行緒建立一個該變數的副本,避免併發訪問的執行緒安全問題;
private ThreadLocal<T> threadLocal = new ThreadLocal<>();
// 當前執行緒副本中的值
T get();
// 刪除
void remove();
// 設定當前執行緒副本中的值
void set(T value);
與同步機制的區別:
同步機制是為了同步多個執行緒對共享資源的併發訪問,是多執行緒間通訊的有限方式;
ThreadLocal隔離多個執行緒的資料共享,避免多個執行緒間對共享資源的競爭,不需要對多個執行緒進行同步。
執行緒安全集合
通過Collections包裝成執行緒安全集合:
ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap等都是執行緒不安全;如果多個執行緒對這些集合讀,寫時,會破壞這些集合資料的完整性。
Collections提供靜態方法將這些集合包裝成執行緒安全的集合。
synchronizedCollection(Collection<T> c)
synchronizedList<List<T> list>
synchronizedMap(Map<K, V> map)
synchronizedSet(Set<T> set)
HashMap m = Collections.synchronizedMap(new HashMap);
執行緒安全集合:
ConcurrentHashMap, ConcurrentSkipListMap, ConcurrentSkipListSet, ConcurrentLinkedQueue, ConcurrentLinkedDeque
CopyOnWriteArrayList
CopyOnWriteArraySet