標籤:調用 href 一個 資料 鏈表 jstack 退出 幹什麼 週期性任務
Java多線程和高並發總結
wait/notify必須存在於synchronized塊中。
volatile
多線程的記憶體模型:main memory(主存)、working memory(線程棧),在處理資料時,線程會把值從主存load到本地棧,完成操作後再save回去(volatile關鍵詞的作用:每次針對該變數的操作都激發一次load and save)
Thread類最佳實務: 寫的時候最好要設定線程名稱 Thread.name,並設定線程組 ThreadGroup,目的是方便管理。在出現問題的時候,列印線程棧 (jstack -pid) 一眼就可以看出是哪個線程出的問題,這個線程是幹什麼的。
如何擷取線程中異常
Thread t = new Thread(task);
t.setUncaughtException(new UncaughtExceptionHandler(){...});
t.start();
不能用try,catch來擷取線程中的異常
Future和FutureTask
Future是介面,FutureTask是類
Future表示一個非同步計算的結果. 非同步計算是在其他線程進行的, 因此非同步計算的結果, 有可能有值, 也有可能沒有值. 於是, Future就提供了一些方法來處理這種未知狀態:
a. isDone() 非同步任務是否完成, 即否有結果
b. get() 擷取非同步任務結果, 如果非同步任務未完成, 此方法會一直阻塞, 直到非同步方法呼叫完成 或 任務被取消 (調用了cancel()方法)
c. cancel() 取消非同步任務, 如果非同步任務已經完成, 那麼取消失敗(即cancel()方法返回false)
d. isCancelled() 查詢非同步任務是否已被取消
FutureTask就是一個可取消的非同步任務,FutureTask實現了兩個介面,Runnable和Future,所以它既可以作為Runnable被線程執行,又可以作為Future得到Callable的傳回值,那麼這個組合的使用有什麼好處呢?假設有一個很耗時的傳回值需要計算,並且這個傳回值不是立刻需要的話,那麼就可以使用這個組合,用另一個線程去計算傳回值,而當前線程在使用這個傳回值之前可以做其它的操作,等到需要這個傳回值時,再通過Future得到
//1FutureTask<Integer> future = new FutureTask<Object>(callable); new Thread(future).start(); //這裡可以做其他事情 future.get();//阻塞獲得結果 //2public void function() { // 1. init services final ExecutorService executorService = ... // 2. make task FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() { @Override public String call() throws Exception { String result = ""; //do something; return result; } }); // 3. submit task executorService.submit(futureTask); // 4. get result String result = futureTask.get(); // 5. do something with result // ...}
ThreadLocal類
用處:儲存線程的獨立變數。對一個線程類(繼承自Thread) 當使用ThreadLocal維護變數時,ThreadLocal為每個使用該變數的線程提供獨立的變數副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。常用於使用者登入控制,如記錄session資訊。
實現:每個Thread都持有一個TreadLocalMap類型的變數(該類是一個輕量級的Map,功能與map一樣,區別是桶裡放的是entry而不是entry的鏈表。功能還是一個map。)以本身為key,以目標為value。 主要方法是get()和set(T a),set之後在map裡維護一個threadLocal -> a,get時將a返回。ThreadLocal是一個特殊的容器。
並發包:原子類、鎖類、容器類、管理類總結
並發包總結1
並發包總結2
cas和aba
CAS:對於記憶體中的某一個值V,提供一箇舊值A和一個新值B。如果提供的舊值V和A相等就把B寫入V。這個過程是原子性的。
CAS執行結果要麼成功要麼失敗,對於失敗的情形下一班採用不斷重試。或者放棄。
ABA:如果另一個線程修改V值假設原來是A,先修改成B,再修改回成A。當前線程的CAS操作無法分辨當前V值是否發生過變化。
如何解決ABA問題? 通常是使用版本戳來解決這個問題,避免並發中的問題。 CAS是一種樂觀鎖技術
重排序/happens before
JMM中,如果一個操作執行的結果需要對另一個操作可見,那麼這兩個操作之間必須存在happens-before關係。
happen-before原則是JMM中非常重要的原則,它是判斷資料是否存在競爭、線程是否安全的主要依據,保證了多線程環境下的可見度。
happen-before原則解決了重排序帶來的多線程運行問題。
如果兩個操作之間具有happens-before 關係,那麼前一個操作的結果就會對後面一個操作可見。
happens-before原則規則:
- 程式次序規則:一個線程內,按照代碼順序,書寫在前面的操作先行發生於書寫在後面的操作;
- 鎖定規則:一個unLock操作先行發生於後面對同一個鎖額lock操作;
- volatile變數規則:對一個變數的寫操作先行發生於後面對這個變數的讀操作;
- 路由規則:如果操作A先行發生於操作B,而操作B又先行發生於操作C,則可以得出操作A先行發生於操作C;
- 線程啟動規則:Thread對象的start()方法先行發生於此線程的每個一個動作;
- 線程中斷規則:對線程interrupt()方法的調用先行發生於被中斷線程的代碼檢測到中斷事件的發生;
- 線程終結規則:線程中所有的操作都先行發生於線程的終止檢測,我們可以通過Thread.join()方法結束、Thread.isAlive()的傳回值手段檢測到線程已經終止執行;
- 對象終結規則:一個對象的初始化完成先行發生於他的finalize()方法的開始;
線程池
Java通過Executors提供四種線程池,分別為:
(緩衝、固定、定時、單例)
- newCachedThreadPool建立一個可緩衝線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則建立線程。
- newFixedThreadPool 建立一個定長線程池,可控制線程最大並發數,超出的線程會在隊列中等待。
- newScheduledThreadPool 建立一個定長線程池,支援定時及週期性任務執行。(定時任務建議用這個,不要用TimerTask,因為:異常退出所有任務)
- newSingleThreadExecutor 建立一個單線程化的線程池,它只會用唯一的背景工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。
參考文獻
文獻1-並發多線程
文獻2-並發編程指南
文獻3-AQS
文獻4-concurrentHashMap
Java多線程和並發總結