Java多線程和並發總結
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() 異步任務是否完成, 即否有結果
c. cancel() 取消異步任務, 如果異步任務已經完成, 那麽取消失敗(即cancel()方法返回false)
d. isCancelled() 查詢異步任務是否已被取消FutureTask就是一個可取消的異步任務,FutureTask實現了兩個接口,Runnable和Future,所以它既可以作為Runnable被線程執行,又可以作為Future得到Callable的返回值,那麽這個組合的使用有什麽好處呢?假設有一個很耗時的返回值需要計算,並且這個返回值不是立刻需要的話,那麽就可以使用這個組合,用另一個線程去計算返回值,而當前線程在使用這個返回值之前可以做其它的操作,等到需要這個返回值時,再通過Future得到
//1 FutureTask<Integer> future = new FutureTask<Object>(callable); new Thread(future).start(); //這裏可以做其他事情 future.get();//阻塞獲得結果 //2 public 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多線程和並發總結