无招胜有招之多线程
?
? ? ? ?并發和并行的區別:
解釋一:并行是指兩個或者多個事件在同一時刻發生;而并發是指兩個或多個事件在同一時間間隔發生。
解釋二:并行是在不同實體上的多個事件,并發是在同一實體上的多個事件。
解釋三:并行是在一臺處理器上“同時”處理多個任務,并發是在多臺處理器上同時處理多個任務。如 hadoop 分布式集群。
-
線程和進程的區別
線程和進程一樣,都是實現并發的一個基本單位。但線程是比進程更小的執行單位,線程是在進程的基礎之上進行的進一步劃分。多線程指一個進程在執行過程中可以產生多個更小的程序單元,這些更小的單元稱為線程。
進程是程序的一次動態執行過程,他需要經歷從代碼加載、代碼執行到執行完畢的一個完整過程,這個過程也是程序本身從產生、發展到最終消亡的過程。每一個運行的程序就是一個進程,一般而言,進程在系統中獨立存在,擁有自己獨立的資源。
-
線程的實現、線程的狀態、優先級、線程調度、創建線程的多種方式、守護線程
三種實現線程的方式:
?
新建狀態(new)-》就緒狀態(start)-》運行狀態(獲得CPU使用權)-》阻塞狀態(因為某些原因放棄CPU,暫時停止運行)-》死亡狀態(run正常執行完成、線程出現異常或錯誤、調用了線程的stop方法)
線程一旦死亡,不能運行且不能轉換為其他形態。
【補充】yield不會是線程阻塞,只是將線程轉換為就緒狀態,也就是讓線程暫停一下,線程調度器重新調度一次,有可能還會將暫停的程序調度出來繼續執行,這就是線程讓步。
所有處于就緒狀態的線程根據優先級存放在可運行池中,優先級低的運行機會較少,優先級高的運行機會更多。
Thread的setPriority(int newPriority)方法和getPriority()方法用來設置優先級和讀取優先級。
如果一個計算機只有一個CPU,那么在任意時刻只能執行一條指令,每個線程只能獲得CPU使用權才能執行指令。從宏觀上看,多線程的并發運行是每一個線程輪流獲得CPU的使用權,分別執行各自的任務。但在運行池中,會有多個處于就緒狀態的線程在等待CPU,JVM的一項任務就是負責線程的調度,即按照特定的機制為多個線程分配CPU使用權。調度模型分為分時調度模型和搶占式調度模型兩種。
分時調度模型是讓所有線程輪流獲取CPU使用權,平均分配每一個線程占用CPU的時間片。搶占式調度模型是優先讓可運行池中優先級高的線程占用CPU,若運行池中線程優先級相同,則隨機選擇一個線程使用CPU,當它失去CPU使用權時,再隨機選取一個線程獲得CPU使用權。JAVA默認使用搶占式調度模型。
?? 又稱后臺線程,JVM的垃圾回收線程就是典型的后臺線程。即如果前臺線程都死亡, 后臺線程會自動死亡。當整個虛擬機中只剩下后臺線程,程序就沒有繼續運行的必要了, 所以JVM就退出了。
可以調用Thread中的setDaemon(true)。將線程設置為守護線程。另外,Thread類還提供了isDaemon()用于判斷線程是否是守護線程。
-
自己設計線程池、submit() 和execute()、 線程池原理
https://blog.csdn.net/iterzebra/article/details/6758481
即ExecutorService中submit()和execute()的區別
(1)execute()是Executor接口中的方法:
| public?interface?Executor { ????void?execute(Runnable command); } ? |
execute()方法的入參為一個Runnable,返回值為void。
| public interface ExecutorService extends Executor { ... <T> Future<T> submit(Callable<T> task); ? <T> Future<T> submit(Runnable task, T result); ? Future<?> submit(Runnable task); ... } |
submit()方法的入參可以為Callable<T>,也可以為Runnable,而且方法有返回值Future<T>。
execute()和submit()方法的區別:
(1).?接收的參數不一樣;
(2).?submit()有返回值,而execute()沒有;
例如,有個validation的task,希望該task執行完后告訴我它的執行結果,是 成功還是失敗,然后繼續下面的操作。
(3).?submit()可以進行Exception處理;
例如,如果task里會拋出checked或者unchecked exception,而你又希望 外面的調用者能夠感知這些exception并做出及時的處理,那么就需要用到submit,通 過對 Future.get()進行拋出異常的捕獲,然后對其進行處理。
程序啟動一個新線程的成本是比較高的,因為它涉及要與才做系統進行交互。而使用線程池可以很好地提高性能,尤其是當程序中需要創建大量生存期很短的線程時,更應該考慮到線程池,線程池中的每一個線程代碼結束后,并不會死亡,而是再次回到線程池中成為空閑狀態,等待下一個對象來使用。
使用線程池好處:1.降低資源消耗2.提高響應速度3.提高線程的可管理性
-
為什么不允許使用Executors創建線程池
線程池不允許使用Executors去創建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。
-
死鎖、死鎖如何排查、線程安全和內存模型的關系
不同線程分別占用對方需要的同步資源不放棄,都在等待對方放棄自己需要的同步資源,就形成了線程的死鎖。
排查:
在window打開 jvisualvm,jvisualvm是一個圖形化的監控工具!
在windons命令窗口 ,輸出 jvisualvm
線程安全和內存模型的關系
https://blog.csdn.net/mikeoperfect/article/details/79133899
-
ThreadLocal變量
線程相關類ThreadLocal并非一個線程Thread,而是ThreadLocalVariable(線程局部變量),ThreadLocal功用非常簡單,就是為每一個使用該變量的線程都提供一個變量值的副本,是java中一種較為特殊的線程綁定機制,是每一個線程都可以獨立改變自己的副本,而不會和其他線程副本沖突。
核心意思:ThreadLocal 提供了線程本地的實例。它與普通變量的區別在于,每個使用該變量的線程都會初始化一個完全獨立的實例副本。ThreadLocal 變量通常被private static修飾。當一個線程結束時,它所使用的所有 ThreadLocal 相對的實例副本都可被回收。
【補充:】在Spring中的單例Bean的線程安全問題。
兩種解決方案:
1.在Bean對象中盡量避免定義可變的成員變量(不太現實)。
2.在類中定義一個ThreadLocal成員變量,將需要的可變成員變量保存在 ThreadLocal 中(推薦的一種方式)。
?
-
Executor創建線程池的幾種方式:
newFixedThreadPool(int nThreads)創建一個可重用固定線程數的線程池,以共享的無界隊列方式來運行這些線程。
newCachedThreadPool()創建一個可根據需要創建新線程的線程池,在以前構造的線程可用時將重用它們。
newSingleThreadExecutor()創建一個使用單個worker線程的Executor,以無界隊列方式來運行該線程。
newScheduledThreadP ool(int corePoolSize)創建一個線程池,可以調度命令在給定的延遲之后運行,或定期執行。
【補充】
有界隊列:就是有固定大小的隊列。比如設定了固定大小的 LinkedBlockingQueue,又或者大小為 0,只是在生產者和消費者中做中轉用的 SynchronousQueue。
無界隊列:指的是沒有設置固定大小的隊列。這些隊列的特點是可以直接入列,直到溢出。當然現實幾乎不會有到這么大的容量(超過 Integer.MAX_VALUE),所以從使用者的體驗上,就相當于 “無界”。比如沒有設定固定大小的 LinkedBlockingQueue。
-
ThreadPoolExecutor創建線程池、拒絕策略
?
拿最后一個為例:
?
拒絕策略:
1.AbortPolicy
該策略是線程池的默認策略。使用該策略時,如果線程池隊列滿了丟掉這個任務并且拋出RejectedExecutionException異常。
2.DiscardPolicy
這個策略和AbortPolicy的slient版本,如果線程池隊列滿了,會直接丟掉這個任務并且不會有任何異常。
3.DiscardOldestPolicy
這個策略從字面上也很好理解,丟棄最老的。也就是說如果隊列滿了,會將最早進入隊列的任務刪掉騰出空間,再嘗試加入隊列。
因為隊列是隊尾進,隊頭出,所以隊頭元素是最老的,因此每次都是移除對頭元素后再嘗試入隊。
4.CallerRunsPolicy
使用此策略,如果添加到線程池失敗,那么主線程會自己去執行該任務,不會等待線程池中的線程去執行。就像是個急脾氣的人,我等不到別人來做這件事就干脆自己干。
5.自定義拒絕策略
實現RejectedExecutionHandler接口
-
線程池關閉的方式
Java提供的對ExecutorService的關閉方式有兩種,一種是調用其shutdown()方法,另一種是調用shutdownNow()方法。這兩者是有區別的。
shutdown:
1、調用之后不允許繼續往線程池內繼續添加線程;
2、線程池的狀態變為SHUTDOWN狀態;
3、所有在調用shutdown()方法之前提交到ExecutorSrvice的任務都會執行;
4、一旦所有線程結束執行當前任務,ExecutorService才會真正關閉。
shutdownNow():
1、該方法返回尚未執行的 task 的 List;
2、線程池的狀態變為STOP狀態;
3、阻止所有正在等待啟動的任務, 并且停止當前正在執行的任務。
簡單點來說,就是:
shutdown()調用后,不可以再 submit 新的 task,已經 submit 的將繼續執行
shutdownNow()調用后,試圖停止當前正在執行的 task,并返回尚未執行的 task 的 list
?
?
?
?
?
?
?
總結
- 上一篇: 无招胜有招之锁
- 下一篇: Win7系统中Cookie位置