建設一個電子商務網(wǎng)站seo 優(yōu)化公司
本文已經(jīng)收錄到Github倉庫,該倉庫包含計算機基礎、Java基礎、多線程、JVM、數(shù)據(jù)庫、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服務、設計模式、架構(gòu)、校招社招分享等核心知識點,歡迎star~
Github地址:https://github.com/Tyson0314/Java-learning
操作系統(tǒng)的四個特性?
并發(fā):同一段時間內(nèi)多個程序執(zhí)行(與并行區(qū)分,并行指的是同一時刻有多個事件,多處理器系統(tǒng)可以使程序并行執(zhí)行)
共享:系統(tǒng)中的資源可以被內(nèi)存中多個并發(fā)執(zhí)行的進線程共同使用
虛擬:通過分時復用(如分時系統(tǒng))以及空分復用(如虛擬內(nèi)存)技術(shù)把一個物理實體虛擬為多個
異步:系統(tǒng)進程用一種走走停停的方式執(zhí)行,(并不是一下子走完),進程什么時候以怎樣的速度向前推進是不可預知的
進程線程
進程是指一個內(nèi)存中運行的應用程序,每個進程都有自己獨立的一塊內(nèi)存空間。
線程是比進程更小的執(zhí)行單位,它是在一個進程中獨立的控制流,一個進程可以啟動多個線程,每條線程并行執(zhí)行不同的任務。
進程和線程的區(qū)別如下:
- 調(diào)度:進程是資源管理的基本單位,線程是程序執(zhí)行的基本單位。
- 切換:線程上下文切換比進程上下文切換要快得多。
- 擁有資源: 進程是擁有資源的一個獨立單位,線程不擁有系統(tǒng)資源,但是可以訪問隸屬于進程的資源。
- 系統(tǒng)開銷: 創(chuàng)建或撤銷進程時,系統(tǒng)都要為之分配或回收系統(tǒng)資源,如內(nèi)存空間,I/O設備等,OS所付出的開銷顯著大于在創(chuàng)建或撤銷線程時的開銷,進程切換的開銷也遠大于線程切換的開銷。
并發(fā)和并行
并發(fā)就是在一段時間內(nèi),多個任務都會被處理;但在某一時刻,只有一個任務在執(zhí)行。單核處理器可以做到并發(fā)。比如有兩個進程A
和B
,A
運行一個時間片之后,切換到B
,B
運行一個時間片之后又切換到A
。因為切換速度足夠快,所以宏觀上表現(xiàn)為在一段時間內(nèi)能同時運行多個程序。最全面的Java面試網(wǎng)站
并行就是在同一時刻,有多個任務在執(zhí)行。這個需要多核處理器才能完成,在微觀上就能同時執(zhí)行多條指令,不同的程序被放到不同的處理器上運行,這個是物理上的多個進程同時進行。
多線程相較單線程的好處
1、并發(fā)提升程序執(zhí)行效率
2、提升CPU利用率,訪存的時候可以切換線程來執(zhí)行
3、更快的響應速度,可以有專門的線程來監(jiān)聽用戶請求和專門的線程來處理請求。比如監(jiān)聽線程和工作線程是兩個線程,這樣監(jiān)聽就負責監(jiān)聽,工作的就負責工作,監(jiān)聽到用戶請求馬上把請求轉(zhuǎn)到工作線程去處理,監(jiān)聽線程繼續(xù)監(jiān)聽
什么是協(xié)程?
協(xié)程是一種用戶態(tài)的輕量級線程。
協(xié)程不是由操作系統(tǒng)內(nèi)核管理,而是完全由用戶程序所控制,這樣帶來的好處就是性能得到了很大的提升,不會像線程切換那樣消耗資源。
協(xié)程可以理解為可以暫停執(zhí)行的函數(shù)。它擁有自己的寄存器上下文和棧。協(xié)程調(diào)度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復先前保存的寄存器上下文和棧,直接操作棧則基本沒有內(nèi)核切換的開銷,可以不加鎖的訪問全局變量,所以上下文的切換非??臁?/p>
線程和協(xié)程有什么區(qū)別呢?
1、線程是搶占式,而協(xié)程是非搶占式的,所以需要用戶自己釋放使用權(quán)來切換到其他協(xié)程,因此同一時間其實只有一個協(xié)程擁有運行權(quán),相當于單線程的能力。
2、線程是協(xié)程的資源。協(xié)程通過 可以關(guān)聯(lián)任意線程或線程池的執(zhí)行器(Interceptor)來間接使用線程的資源的。
進程通信
進程間通信方式有以下幾種:
1、管道通信
匿名管道( pipe ):管道是一種半雙工的通信方式,數(shù)據(jù)只能單向流動,而且只能在具有親緣關(guān)系的進程間使用。進程的親緣關(guān)系通常是指父子進程關(guān)系。
有名管道是半雙工的通信方式,數(shù)據(jù)只能單向流動。
2、消息隊列
3、共享內(nèi)存。共享內(nèi)存是最快的 IPC 方式,它是針對其他進程間通信方式運行效率低而專門設計的。它往往與其他通信機制,如信號量,配合使用,來實現(xiàn)進程間的同步和通信。
4、信號量。信號量是一個計數(shù)器,可以用來控制多個進程對共享資源的訪問。它常作為一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。因此,主要作為進程間以及同一進程內(nèi)不同線程之間的同步手段。
什么是死鎖?
死鎖是指兩個或兩個以上的線程在執(zhí)行過程中,因爭奪資源而造成的一種互相等待的現(xiàn)象。若無外力作用,它們都將無法推進下去。
如下圖所示,線程 A 持有資源 2,線程 B 持有資源 1,他們同時都想申請對方持有的資源,所以這兩個線程就會互相等待而進入死鎖狀態(tài)。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-xqlJiG1P-1678927107037)(http://img.topjavaer.cn/img/死鎖.png)]
下面通過例子說明線程死鎖,代碼來自并發(fā)編程之美。
public class DeadLockDemo {private static Object resource1 = new Object();//資源 1private static Object resource2 = new Object();//資源 2public static void main(String[] args) {new Thread(() -> {synchronized (resource1) {System.out.println(Thread.currentThread() + "get resource1");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread() + "waiting get resource2");synchronized (resource2) {System.out.println(Thread.currentThread() + "get resource2");}}}, "線程 1").start();new Thread(() -> {synchronized (resource2) {System.out.println(Thread.currentThread() + "get resource2");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread() + "waiting get resource1");synchronized (resource1) {System.out.println(Thread.currentThread() + "get resource1");}}}, "線程 2").start();}
}
代碼輸出如下:
Thread[線程 1,5,main]get resource1
Thread[線程 2,5,main]get resource2
Thread[線程 1,5,main]waiting get resource2
Thread[線程 2,5,main]waiting get resource1
線程 A 通過 synchronized
(resource1) 獲得 resource1 的監(jiān)視器鎖,然后通過 Thread.sleep(1000)
。讓線程 A 休眠 1s 為的是讓線程 B 得到執(zhí)行然后獲取到 resource2 的監(jiān)視器鎖。線程 A 和線程 B 休眠結(jié)束了都開始企圖請求獲取對方的資源,然后這兩個線程就會陷入互相等待的狀態(tài),這也就產(chǎn)生了死鎖。
死鎖怎么產(chǎn)生?怎么避免?
死鎖產(chǎn)生的四個必要條件:
-
互斥:一個資源每次只能被一個進程使用
-
請求與保持:一個進程因請求資源而阻塞時,不釋放獲得的資源
-
不剝奪:進程已獲得的資源,在未使用之前,不能強行剝奪
-
循環(huán)等待:進程之間循環(huán)等待著資源
避免死鎖的方法:
- 互斥條件不能破壞,因為加鎖就是為了保證互斥
- 一次性申請所有的資源,避免線程占有資源而且在等待其他資源
- 占有部分資源的線程進一步申請其他資源時,如果申請不到,主動釋放它占有的資源
- 按序申請資源
進程調(diào)度策略有哪幾種?
-
先來先服務:非搶占式的調(diào)度算法,按照請求的順序進行調(diào)度。有利于長作業(yè),但不利于短作業(yè),因為短作業(yè)必須一直等待前面的長作業(yè)執(zhí)行完畢才能執(zhí)行,而長作業(yè)又需要執(zhí)行很長時間,造成了短作業(yè)等待時間過長。另外,對
I/O
密集型進程也不利,因為這種進程每次進行I/O
操作之后又得重新排隊。 -
短作業(yè)優(yōu)先:非搶占式的調(diào)度算法,按估計運行時間最短的順序進行調(diào)度。長作業(yè)有可能會餓死,處于一直等待短作業(yè)執(zhí)行完畢的狀態(tài)。因為如果一直有短作業(yè)到來,那么長作業(yè)永遠得不到調(diào)度。
-
最短剩余時間優(yōu)先:最短作業(yè)優(yōu)先的搶占式版本,按剩余運行時間的順序進行調(diào)度。 當一個新的作業(yè)到達時,其整個運行時間與當前進程的剩余時間作比較。如果新的進程需要的時間更少,則掛起當前進程,運行新的進程。否則新的進程等待。
-
時間片輪轉(zhuǎn):將所有就緒進程按
FCFS
的原則排成一個隊列,每次調(diào)度時,把CPU
時間分配給隊首進程,該進程可以執(zhí)行一個時間片。當時間片用完時,由計時器發(fā)出時鐘中斷,調(diào)度程序便停止該進程的執(zhí)行,并將它送往就緒隊列的末尾,同時繼續(xù)把CPU
時間分配給隊首的進程。時間片輪轉(zhuǎn)算法的效率和時間片的大小有很大關(guān)系:因為進程切換都要保存進程的信息并且載入新進程的信息,如果時間片太小,會導致進程切換得太頻繁,在進程切換上就會花過多時間。 而如果時間片過長,那么實時性就不能得到保證。
-
優(yōu)先級調(diào)度:為每個進程分配一個優(yōu)先級,按優(yōu)先級進行調(diào)度。為了防止低優(yōu)先級的進程永遠等不到調(diào)度,可以隨著時間的推移增加等待進程的優(yōu)先級。
進程有哪些狀態(tài)?
進程一共有5
種狀態(tài),分別是創(chuàng)建、就緒、運行(執(zhí)行)、終止、阻塞。
- 運行狀態(tài)就是進程正在
CPU
上運行。在單處理機環(huán)境下,每一時刻最多只有一個進程處于運行狀態(tài)。 - 就緒狀態(tài)就是說進程已處于準備運行的狀態(tài),即進程獲得了除
CPU
之外的一切所需資源,一旦得到CPU
即可運行。 - 阻塞狀態(tài)就是進程正在等待某一事件而暫停運行,比如等待某資源為可用或等待
I/O
完成。即使CPU
空閑,該進程也不能運行。
運行態(tài)→阻塞態(tài):往往是由于等待外設,等待主存等資源分配或等待人工干預而引起的。
阻塞態(tài)→就緒態(tài):則是等待的條件已滿足,只需分配到處理器后就能運行。
運行態(tài)→就緒態(tài):不是由于自身原因,而是由外界原因使運行狀態(tài)的進程讓出處理器,這時候就變成就緒態(tài)。例如時間片用完,或有更高優(yōu)先級的進程來搶占處理器等。
就緒態(tài)→運行態(tài):系統(tǒng)按某種策略選中就緒隊列中的一個進程占用處理器,此時就變成了運行態(tài)。
操作系統(tǒng)里的內(nèi)存碎片怎么理解?
內(nèi)存碎片通常分為內(nèi)部碎片和外部碎片:
- 內(nèi)部碎片是由于采用固定大小的內(nèi)存分區(qū),當一個進程不能完全使用分給它的固定內(nèi)存區(qū)域時就會產(chǎn)生內(nèi)部碎片。通常內(nèi)部碎片難以完全避免
- 外部碎片是由于某些未分配的連續(xù)內(nèi)存區(qū)域太小,以至于不能滿足任意進程的內(nèi)存分配請求,從而不能被進程利用的內(nèi)存區(qū)域。
有什么解決辦法?
現(xiàn)在普遍采取的內(nèi)存分配方式是段頁式內(nèi)存分配。將內(nèi)存分為不同的段,再將每一段分成固定大小的頁。通過頁表機制,使段內(nèi)的頁可以不必連續(xù)處于同一內(nèi)存區(qū)域。
虛擬內(nèi)存
虛擬存儲器就是具有請求調(diào)入功能,能從邏輯上對內(nèi)存容量加以擴充的一種存儲器系統(tǒng),虛擬內(nèi)存有多次性,對換性和虛擬性三個特征,它可以將程序分多次調(diào)入內(nèi)存,使得在較小的用戶空間可以執(zhí)行較大的用戶程序,所以同時容納更多的進程并發(fā)執(zhí)行,從而提高系統(tǒng)的吞吐量。發(fā)生缺頁時可以調(diào)入一個段也可以調(diào)入一個頁,取決于內(nèi)存的存儲管理方式。虛擬性表示虛擬內(nèi)存和物理內(nèi)存的映射。
Linux下,進程不能直接讀寫內(nèi)存物理地址,只能訪問【虛擬內(nèi)存地址】。操作系統(tǒng)會把虛擬內(nèi)存地址–>物理地址。
虛擬內(nèi)存解決有限的內(nèi)存空間加載較大應用程序的問題,根據(jù)需要在內(nèi)存和磁盤之間來回傳送數(shù)據(jù)。
通過段頁表的形式,虛擬內(nèi)存中取一段連續(xù)的內(nèi)存空間映射到主內(nèi)存中,主內(nèi)存空間的程序段可以不連續(xù) 。
什么是分頁?
把內(nèi)存空間劃分為大小相等且固定的塊,作為主存的基本單位。因為程序數(shù)據(jù)存儲在不同的頁面中,而頁面又離散的分布在內(nèi)存中,因此需要一個頁表來記錄映射關(guān)系,以實現(xiàn)從頁號到物理塊號的映射。
訪問分頁系統(tǒng)中內(nèi)存數(shù)據(jù)需要兩次的內(nèi)存訪問 (一次是從內(nèi)存中訪問頁表,從中找到指定的物理塊號,加上頁內(nèi)偏移得到實際物理地址;第二次就是根據(jù)第一次得到的物理地址訪問內(nèi)存取出數(shù)據(jù))。
什么是分段?
分頁是為了提高內(nèi)存利用率,而分段是為了滿足程序員在編寫代碼的時候的一些邏輯需求(比如數(shù)據(jù)共享,數(shù)據(jù)保護,動態(tài)鏈接等)。
分段內(nèi)存管理當中,地址是二維的,一維是段號,二維是段內(nèi)地址;其中每個段的長度是不一樣的,而且每個段內(nèi)部都是從0開始編址的。由于分段管理中,每個段內(nèi)部是連續(xù)內(nèi)存分配,但是段和段之間是離散分配的,因此也存在一個邏輯地址到物理地址的映射關(guān)系,相應的就是段表機制。
分頁和分段有什區(qū)別?
- 分頁對程序員是透明的,但是分段需要程序員顯式劃分每個段。
- 分頁的地址空間是一維地址空間,分段是二維的。
- 頁的大小不可變,段的大小可以動態(tài)改變。
- 分頁主要用于實現(xiàn)虛擬內(nèi)存,從而獲得更大的地址空間;分段主要是為了使程序和數(shù)據(jù)可以被劃分為邏輯上獨立的地址空間并且有助于共享和保護。
頁面置換算法
為什么要頁面置換:
因為應用程序是分多次裝入內(nèi)存的,所以運行到一定的時間,一定會發(fā)生缺頁。地址映射的過程中,如果頁面中發(fā)現(xiàn)要訪問的頁面不在內(nèi)存中,會產(chǎn)生缺頁中斷。此時操作系統(tǒng)必須在內(nèi)存里選擇一個頁面把他移出內(nèi)存,為即將調(diào)入的頁面讓出空間。選擇淘汰哪一頁的規(guī)則就是頁面置換算法
幾種頁面置換算法:
最佳置換算法(理想):將當前頁面中在未來最長時間內(nèi)不會被訪問的頁置換出去
先進先出:淘汰最早調(diào)入的頁面
最近最久未使用 LRU:每個頁面有一個t來記錄上次頁面被訪問直到現(xiàn)在,每次置換時置換t值最大的頁面(用寄存器或棧實現(xiàn))
時鐘算法clock(也被稱為最近未使用算法NRU):頁面設置訪問為,將頁面鏈接為一個環(huán)形列表,每個頁有一個訪問位0/1, 1表示又一次獲救的機會,下次循環(huán)指針指向它時可以免除此次置換,但是會把訪問位置為0, 代表他下次如果碰到循環(huán)指針就該被置換了。頁面被訪問的時候訪問位設為1。頁面置換的時候,如果當前指針的訪問位為0,置換,否則將這個值置為0,循環(huán)直到遇到訪問位為0的頁面。
改進型Clock算法:在clock算法的基礎上添加一個修改位,優(yōu)先替換訪問位和修改位都是0的頁面,其次替換訪問位為0修改位為1的頁面。
最少使用算法LFU:設置寄存器記錄頁面被訪問次數(shù),每次置換當前訪問次數(shù)最少的。
用戶態(tài)和內(nèi)核態(tài)
內(nèi)核態(tài):cpu可以訪問內(nèi)存的所有數(shù)據(jù),包括外圍設備,例如硬盤,網(wǎng)卡,cpu也可以將自己從一個程序切換到另一個程序。
用戶態(tài):只能受限的訪問內(nèi)存,且不允許訪問外圍設備,占用cpu的能力被剝奪,cpu資源可以被其他程序獲取。
最大的區(qū)別就是權(quán)限不同,在運行在用戶態(tài)下的程序不能直接訪問操作系統(tǒng)內(nèi)核數(shù)據(jù)結(jié)構(gòu)和程序。
為什么要有這兩種狀態(tài)?
內(nèi)核速度快但是資源有限,能控制的進程數(shù)不多,所以需要速度慢一些的用戶態(tài)協(xié)助,但是為了避免用戶態(tài)被惡意利用,所以限制了用戶態(tài)程序的權(quán)限。
需要限制不同的程序之間的訪問能力,防止他們獲取別的程序的內(nèi)存數(shù)據(jù),或者獲取外圍設備的數(shù)據(jù),并發(fā)送到網(wǎng)絡,CPU劃分出兩個權(quán)限等級 – 用戶態(tài)和內(nèi)核態(tài)。
什么時候轉(zhuǎn)換
1、系統(tǒng)調(diào)用:
用戶進程主動發(fā)起的。用戶態(tài)進程通過系統(tǒng)調(diào)用申請使用操作系統(tǒng)提供的服務程序完成工作,比如fork()就是執(zhí)行一個創(chuàng)建新進程的系統(tǒng)調(diào)用
用戶程序使用系統(tǒng)調(diào)用,系統(tǒng)調(diào)用會轉(zhuǎn)換為內(nèi)核態(tài)并調(diào)用操作系統(tǒng)
2、發(fā)生異常:
會從當前運行進程切換到處理次此異常的內(nèi)核相關(guān)程序中
3、外圍設備的中斷:
所有程序都運行在用戶態(tài),但在從硬盤讀取數(shù)據(jù)、或從鍵盤輸入時,這些事情只有操作系統(tǒng)能做,程序需要向操作系統(tǒng)請求以程序的名義來執(zhí)行這些操作。這個時候用戶態(tài)程序切換到內(nèi)核態(tài)。
什么是緩沖區(qū)溢出?有什么危害?
緩沖區(qū)溢出是指當計算機向緩沖區(qū)填充數(shù)據(jù)時超出了緩沖區(qū)本身的容量,溢出的數(shù)據(jù)覆蓋在合法數(shù)據(jù)上。
危害有以下兩點:
- 程序崩潰,導致拒絕額服務
- 跳轉(zhuǎn)并且執(zhí)行一段惡意代碼
造成緩沖區(qū)溢出的主要原因是程序中沒有仔細檢查用戶輸入。
IO多路復用
IO多路復用是指內(nèi)核一旦發(fā)現(xiàn)進程指定的一個或者多個IO條件準備讀取,它就通知該進程。IO多路復用適用如下場合:
- 當客戶處理多個描述字時(一般是交互式輸入和網(wǎng)絡套接口),必須使用I/O復用。
- 當一個客戶同時處理多個套接口時,而這種情況是可能的,但很少出現(xiàn)。
- 如果一個TCP服務器既要處理監(jiān)聽套接口,又要處理已連接套接口,一般也要用到I/O復用。
- 如果一個服務器即要處理TCP,又要處理UDP,一般要使用I/O復用。
- 如果一個服務器要處理多個服務或多個協(xié)議,一般要使用I/O復用。
- 與多進程和多線程技術(shù)相比,I/O多路復用技術(shù)的最大優(yōu)勢是系統(tǒng)開銷小,系統(tǒng)不必創(chuàng)建進程/線程,也不必維護這些進程/線程,從而大大減小了系統(tǒng)的開銷。
最后給大家分享一個Github倉庫,上面有大彬整理的300多本經(jīng)典的計算機書籍PDF,包括C語言、C++、Java、Python、前端、數(shù)據(jù)庫、操作系統(tǒng)、計算機網(wǎng)絡、數(shù)據(jù)結(jié)構(gòu)和算法、機器學習、編程人生等,可以star一下,下次找書直接在上面搜索,倉庫持續(xù)更新中~
Github地址:https://github.com/Tyson0314/java-books