線程池管理線程,四核八線程和六核十二線程 今天小豆培訓網學歷教育小編就來給大家簡單介紹一下。
本文關鍵字:
線程 , 線程池 , 單線程 , 多線程 , 線程池的好處 , 線程回收 , 創建方式 , 核心參數 , 底層機制 , 拒絕策略 , 參數設置 , 動態監控 , 線程隔離
線程和線程池相關的知識,是Java學習或者面試中一定會遇到的知識點,本篇我們會從線程和進程,并行與并發,單線程和多線程等,一直講解到線程池,線程池的好處,創建方式,重要的核心參數,幾個重要的方法,底層實現,拒絕策略,參數設置,動態調整,線程隔離等等。主要的大綱如下:

Executor 是頂級接口,里面只有一個方法 execute(Runnable command) ,定義的是調度線程池來執行任務,它定義了線程池的基本規范,執行任務是它的天職。
ExecutorServiceExecutorService 繼承了 Executor ,但是它仍然是一個接口,它多了一些方法:

一般而言,上面的拒絕策略都不會特別理想,一般要是任務滿了,首先需要做的就是看任務是不是必要的,如果非必要,非核心,可以考慮拒絕掉,并報錯提醒,如果是必須的,必須把它保存起來,不管是使用mq消息,還是其他手段,不能丟任務。在這些過程中,日志是非常必要的。既要保護線程池,也要對業務負責。
線程池監控與動態調整線程池提供了一些API,可以動態獲取線程池的狀態,并且還可以設置線程池的參數,以及狀態:
查看線程池的狀態:

關于這一點,美團的線程池文章講得很清楚,甚至做了一個實時調整線程池參數的平臺,可以進行跟蹤監控,線程池活躍度、任務的執行Transaction(頻率、耗時)、Reject異常、線程池內部統計信息等等。這里我就不展開了,原文: https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html ,這是我們可以參考的思路。
線程池隔離線程隔離,很多同學可能知道,就是不同的任務放在不同的線程里面運行,而線程池隔離,一般是按照業務類型來隔離,比如訂單的處理線程放在一個線程池,會員相關的處理放在一個線程池。
也可以通過核心和非核心來隔離,核心處理流程放在一起,非核心放在一起,兩個使用不一樣的參數,不一樣的拒絕策略,盡量保證多個線程池之間不影響,并且最大可能保住核心線程的運行,非核心線程可以忍受失敗。
Hystrix 里面運用到這個技術, Hystrix 的線程隔離技術,來防止不同的網絡請求之間的雪崩,即使依賴的一個服務的線程池滿了,也不會影響到應用程序的其他部分。
原文鏈接:http://www.cnblogs.com/Damaer/p/14914813.html
1、關于垃圾收集的哪些敘述是對的
4.垃圾收集將檢查并釋放不再使用的內存。2、一篇文章帶你深度解析Python線程和進程
使用Python中的線程模塊,能夠同時運行程序的不同部分,并簡化設計。如果你已經入門Python,并且想用線程來提升程序運行速度的話,希望這篇教程會對你有所幫助。
線程與進程
什么是進程
進程是系統進行資源分配和調度的一個獨立單位進程是具有一定獨立功能的程序關于某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位。每個進程都有自己的獨立內存空間,不同進程通過進程間通信來通信。由于進程比較重量,占據獨立的內存,所以上下文進程間的切換開銷(棧、寄存器、虛擬內存、文件句柄等)比較大,但相對比較穩定安全。
什么是線程
CPU調度和分派的基本單位線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。線程間通信主要通過共享內存,上下文切換很快,資源開銷較少,但相比進程不夠穩定容易丟失數據。
進程與線程的關系圖
線程與進程的區別:
進程
現實生活中,有很多的場景中的事情是同時進行的,比如開車的時候手和腳共同來駕駛汽車,比如唱歌跳舞也是同時進行的,再比如邊吃飯邊打電話;試想如果我們吃飯的時候有一個領導來電,我們肯定是立刻就接聽了。但是如果你吃完飯再接聽或者回電話,很可能會被開除。
注意:
多任務的概念
什么叫多任務呢?簡單地說,就是操作系統可以同時運行多個任務。打個比方,你一邊在用瀏覽器上網,一邊在聽MP3,一邊在用Word趕作業,這就是多任務,至少同時有3個任務正在運行。還有很多任務悄悄地在后臺同時運行著,只是桌面上沒有顯示而已。
現在,多核CPU已經非常普及了,但是,即使過去的單核CPU,也可以執行多任務。由于CPU執行代碼都是順序執行的,那么,單核CPU是怎么執行多任務的呢?
答案就是操作系統輪流讓各個任務交替執行,任務1執行0.01秒,切換到任務2,任務2執行0.01秒,再切換到任務3,執行0.01秒,這樣反復執行下去。表面上看,每個任務都是交替執行的,但是,由于CPU的執行速度實在是太快了,我們感覺就像所有任務都在同時執行一樣。
真正的并行執行多任務只能在多核CPU上實現,但是,由于任務數量遠遠多于CPU的核心數量,所以,操作系統也會自動把很多任務輪流調度到每個核心上執行。其實就是CPU執行速度太快啦!以至于我們感受不到在輪流調度。
并行與并發
并行(Parallelism)
并行:指兩個或兩個以上事件(或線程)在同一時刻發生,是真正意義上的不同事件或線程在同一時刻,在不同CPU資源呢上(多核),同時執行。
特點
并發(Concurrency)
指一個物理CPU(也可以多個物理CPU)在若干道程序(或線程)之間多路復用,并發性是對有限物理資源強制行使多用戶共享以提高效率。
特點
multiprocess.Process模塊
process模塊是一個創建進程的模塊,借助這個模塊,就可以完成進程的創建。
語法:Process([group[,target[,name[,args[,kwargs]]]]])
由該類實例化得到的對象,表示一個子進程中的任務(尚未啟動)。
注意:1.必須使用關鍵字方式來指定參數;2.args指定的為傳給target函數的位置參數,是一個元祖形式,必須有逗號。
參數介紹:
group:參數未使用,默認值為None。
target:表示調用對象,即子進程要執行的任務。
args:表示調用的位置參數元祖。
kwargs:表示調用對象的字典。如kwargs={'name':Jack,'age':18}。
name:子進程名稱。
代碼:
除了上面這些開啟進程的方法之外,還有一種以繼承Process的方式開啟進程的方式:
通過上面的研究,我們千方百計實現了程序的異步,讓多個任務可以同時在幾個進程中并發處理,他們之間的運行沒有順序,一旦開啟也不受我們控制。盡管并發編程讓我們能更加充分的利用IO資源,但是也給我們帶來了新的問題。
當多個進程使用同一份數據資源的時候,就會引發數據安全或順序混亂問題,我們可以考慮加鎖,我們以模擬搶票為例,來看看數據安全的重要性。
加鎖可以保證多個進程修改同一塊數據時,同一時間只能有一個任務可以進行修改,即串行的修改。加鎖犧牲了速度,但是卻保證了數據的安全。
因此我們最好找尋一種解決方案能夠兼顧:1、效率高(多個進程共享一塊內存的數據)2、幫我們處理好鎖問題。
mutiprocessing模塊為我們提供的基于消息的IPC通信機制:隊列和管道。隊列和管道都是將數據存放于內存中隊列又是基于(管道+鎖)實現的,可以讓我們從復雜的鎖問題中解脫出來,我們應該盡量避免使用共享數據,盡可能使用消息傳遞和隊列,避免處理復雜的同步和鎖問題,而且在進程數目增多時,往往可以獲得更好的可獲展性(后續擴展該內容)。
線程
Python的threading模塊
Python供了幾個用于多線程編程的模塊,包括thread,threading和Queue等。thread和threading模塊允許程序員創建和管理線程。thread模塊供了基本的線程和鎖的支持,而threading供了更高級別,功能更強的線程管理的功能。Queue模塊允許用戶創建一個可以用于多個線程之間共享數據的隊列數據結構。
python創建和執行線程
創建線程代碼
1.創建方法一:
2.創建方法二:
進程和線程都是實現多任務的一種方式,例如:在同一臺計算機上能同時運行多個QQ(進程),一個QQ可以打開多個聊天窗口(線程)。資源共享:進程不能共享資源,而線程共享所在進程的地址空間和其他資源,同時,線程有自己的棧和棧指針。所以在一個進程內的所有線程共享全局變量,但多線程對全局變量的更改會導致變量值得混亂。
代碼演示:
得到的結果是:
首先需要明確的一點是GIL并不是Python的特性,它是在實現Python解析器(CPython)時所引入的一個概念。就好比C++是一套語言(語法)標準,但是可以用不同的編譯器來編譯成可執行代碼。同樣一段代碼可以通過CPython,PyPy,Psyco等不同的Python執行環境來執行(其中的JPython就沒有GIL)。
那么CPython實現中的GIL又是什么呢?GIL全稱GlobalInterpreterLock為了避免誤導,我們還是來看一下官方給出的解釋:
主要意思為:
因此,解釋器實際上被一個全局解釋器鎖保護著,它確保任何時候都只有一個Python線程執行。在多線程環境中,Python虛擬機按以下方式執行:
由于GIL的存在,Python的多線程不能稱之為嚴格的多線程。因為多線程下每個線程在執行的過程中都需要先獲取GIL,保證同一時刻只有一個線程在運行。
由于GIL的存在,即使是多線程,事實上同一時刻只能保證一個線程在運行,既然這樣多線程的運行效率不就和單線程一樣了嗎,那為什么還要使用多線程呢?
由于以前的電腦基本都是單核CPU,多線程和單線程幾乎看不出差別,可是由于計算機的迅速發展,現在的電腦幾乎都是多核CPU了,最少也是兩個核心數的,這時差別就出來了:通過之前的案例我們已經知道,即使在多核CPU中,多線程同一時刻也只有一個線程在運行,這樣不僅不能利用多核CPU的優勢,反而由于每個線程在多個CPU上是交替執行的,導致在不同CPU上切換時造成資源的浪費,反而會更慢。即原因是一個進程只存在一把gil鎖,當在執行多個線程時,內部會爭搶gil鎖,這會造成當某一個線程沒有搶到鎖的時候會讓cpu等待,進而不能合理利用多核cpu資源。
但是在使用多線程抓取網頁內容時,遇到IO阻塞時,正在執行的線程會暫時釋放GIL鎖,這時其它線程會利用這個空隙時間,執行自己的代碼,因此多線程抓取比單線程抓取性能要好,所以我們還是要使用多線程的。
GIL對多線程Python程序的影響
程序的性能受到計算密集型(CPU)的程序限制和I/O密集型的程序限制影響,那什么是計算密集型和I/O密集型程序呢?
計算密集型:要進行大量的數值計算,例如進行上億的數字計算、計算圓周率、對視頻進行高清解碼等等。這種計算密集型任務雖然也可以用多任務完成,但是花費的主要時間在任務切換的時間,此時CPU執行任務的效率比較低。
IO密集型:涉及到網絡請求(time.sleep())、磁盤IO的任務都是IO密集型任務,這類任務的特點是CPU消耗很少,任務的大部分時間都在等待IO操作完成(因為IO的速度遠遠低于CPU和內存的速度)。對于IO密集型任務,任務越多,CPU效率越高,但也有一個限度。
當然為了避免GIL對我們程序產生影響,我們也可以使用,線程鎖。
Lock&RLock
常用的資源共享鎖機制:有Lock、RLock、Semphore、Condition等,簡單給大家分享下Lock和RLock。
Lock
特點就是執行速度慢,但是保證了數據的安全性
RLock
使用鎖代碼操作不當就會產生死鎖的情況。
什么是死鎖
死鎖:當線程A持有獨占鎖a,并嘗試去獲取獨占鎖b的同時,線程B持有獨占鎖b,并嘗試獲取獨占鎖a的情況下,就會發生AB兩個線程由于互相持有對方需要的鎖,而發生的阻塞現象,我們稱為死鎖。即死鎖是指多個進程因競爭資源而造成的一種僵局,若無外力作用,這些進程都將無法向前推進。
所以,在系統設計、進程調度等方面注意如何不讓這四個必要條件成立,如何確定資源的合理分配算法,避免進程永久占據系統資源。
死鎖代碼
python線程間通信
如果各個線程之間各干各的,確實不需要通信,這樣的代碼也十分的簡單。但這一般是不可能的,至少線程要和主線程進行通信,不然計算結果等內容無法取回。而實際情況中要復雜的多,多個線程間需要交換數據,才能得到正確的執行結果。
python中Queue是消息隊列,提供線程間通信機制,python3中重名為為queue,queue模塊塊下提供了幾個阻塞隊列,這些隊列主要用于實現線程通信。
在queue模塊下主要提供了三個類,分別代表三種隊列,它們的主要區別就在于進隊列、出隊列的不同。
簡單代碼演示
此時代碼會阻塞,因為queue中內容已滿,此時可以在第四個queue.put('蘋果')后面添加timeout,則成為queue.put('蘋果',timeout=1)如果等待1秒鐘仍然是滿的就會拋出異常,可以捕獲異常。
同理如果隊列是空的,無法獲取到內容默認也會阻塞,如果不阻塞可以使用queue.get_nowait()。
在掌握了Queue阻塞隊列的特性之后,在下面程序中就可以利用Queue來實現線程通信了。
下面演示一個生產者和一個消費者,當然都可以多個
使用queue模塊,可在線程間進行通信,并保證了線程安全。
協程
協程,又稱微線程,纖程。英文名Coroutine。
協程是python個中另外一種實現多任務的方式,只不過比線程更小占用更小執行單元(理解為需要的資源)。為啥說它是一個執行單元,因為它自帶CPU上下文。這樣只要在合適的時機,我們可以把一個協程切換到另一個協程。只要這個過程中保存或恢復CPU上下文那么程序還是可以運行的。
通俗的理解:在一個線程中的某個函數,可以在任何地方保存當前函數的一些臨時變量等信息,然后切換到另外一個函數中執行,注意不是通過調用函數的方式做到的,并且切換的次數以及什么時候再切換到原來的函數都由開發者自己確定。
在實現多任務時,線程切換從系統層面遠不止保存和恢復CPU上下文這么簡單。操作系統為了程序運行的高效性每個線程都有自己緩存Cache等等數據,操作系統還會幫你做這些數據的恢復操作。所以線程的切換非常耗性能。但是協程的切換只是單純的操作CPU的上下文,所以一秒鐘切換個上百萬次系統都抗的住。
greenlet與gevent
為了更好使用協程來完成多任務,除了使用原生的yield完成模擬協程的工作,其實python還有的greenlet模塊和gevent模塊,使實現協程變的更加簡單高效。
greenlet雖說實現了協程,但需要我們手工切換,太麻煩了,gevent是比greenlet更強大的并且能夠自動切換任務的模塊。
其原理是當一個greenlet遇到IO(指的是inputoutput輸入輸出,比如網絡、文件操作等)操作時,比如訪問網絡,就自動切換到其他的greenlet,等到IO操作完成,再在適當的時候切換回來繼續執行。
模擬耗時操作:
如果有耗時操作也可以換成,gevent中自己實現的模塊,這時候就需要打補丁了。
使用協程完成一個簡單的二手房信息的爬蟲代碼吧!
以下文章來源于Python專欄,作者宋宋
文章鏈接:https://mp.weixin.qq.com/s/2r3_ipU3HjdA5VnqSHjUnQ
3、什么是線程池,如何使用,為什么要用?
一、線程池的作用:
線程池作用就是限制系統中執行線程的數量。
根據系統的環境情況,可以自動或手動設置線程數量,達到運行的最佳效果;少了浪費了系統資源,多了造成系統擁擠效率不高。用線程池控制線程數量,其他線程排隊等候。一個任務執行完畢,再從隊列的中取最前面的任務開始執行。若隊列中沒有等待進程,線程池的這一資源處于等待。當一個新任務需要運行時,如果線程池中有等待的工作線程,就可以開始運行了;否則進入等待隊列。
二、如何使用:
要配置一個線程池是比較復雜的,尤其是對于線程池的原理不是很清楚的情況下,很有可能配置的線程池不是較優的,因此在Executors類里面提供了一些靜態工廠,生成一些常用的線程池。
1. newSingleThreadExecutor創建一個單線程的線程池。這個線程池只有一個線程在工作,也就是相當于單線程串行執行所有任務。如果這個唯一的線程因為異常結束,那么會有一個新的線程來替代它。此線程池保證所有任務的執行順序按照任務的提交順序執行。
2.newFixedThreadPool創建固定大小的線程池。每次提交一個任務就創建一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,如果某個線程因為執行異常而結束,那么線程池會補充一個新線程。
3. newCachedThreadPool創建一個可緩存的線程池。如果線程池的大小超過了處理任務所需要的線程,那么就會回收部分空閑(60秒不執行任務)的線程,當任務數增加時,此線程池又可以智能的添加新線程來處理任務。此線程池不會對線程池大小做限制,線程池大小完全依賴于操作系統(或者說JVM)能夠創建的最大線程大小。4.newScheduledThreadPool創建一個大小無限的線程池。此線程池支持定時以及周期性執行任務的需求。
三、為什么要用線程池:
1.減少了創建和銷毀線程的次數,每個工作線程都可以被重復利用,可執行多個任務。2.可以根據系統的承受能力,調整線程池中工作線線程的數目,防止因為消耗過多的內存,而把服務器累趴下(每個線程需要大約1MB內存,線程開的越多,消耗的內存也就越大,最后死機)。
Java里面線程池的頂級接口是Executor,但是嚴格意義上講Executor并不是一個線程池,而只是一個執行線程的工具。真正的線程池接口是ExecutorService。