技術問答集錦(四)

猿碼道發表於2018-01-22

2 TCP 與 UDP

  1. 網路層:IP協議、ICMP協議、ARP協議、RARP協議和BOOTP協議;
  2. 傳輸層:TCP協議與UDP協議;
  3. 應用層:FTP、HTTP、TELNET、SMTP、DNS等協議;

HTTP是應用層協議,其傳輸都是被包裝成TCP協議傳輸。可以用SOCKET實現HTTP。

Socket是一組介面,可以實現TCP,也可以實現UDP。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/UDP協議族隱藏在Socket介面後面,對使用者來說,一組簡單的介面就是全部,讓Socket去組織資料,以符合指定的協議。

網路協議結構

  1. TCP --- 傳輸控制協議,提供的是面向連線、可靠的位元組流服務。當客戶和伺服器彼此交換資料前,必須先在雙方之間建立一個TCP連線,之後才能傳輸資料。TCP提供超時重發,丟棄重複資料,檢驗資料,流量控制等功能,保證資料能從一端傳到另一端。 理想狀態下,TCP連線一旦建立,在通訊雙方中的任何一方主動關閉連線前,TCP 連線都將被一直保持下去。斷開連線時伺服器和客戶端均可以主動發起斷開TCP連線的請求。

  2. UDP --- 使用者資料包協議,是一個無連線的簡單的面向資料包的運輸層協議。UDP不提供可靠性,它只是把應用程式傳給IP層的資料包傳送出去,但是並不能保證它們能到達目的地。由於UDP在傳輸資料包前不用在客戶和伺服器之間建立一個連線,且沒有超時重發等機制,故而傳輸速度很快。

  3. TCP連線的三次握手:要了解TCP,一定要知道 "三次握手,四次拜拜" 所謂的三次握手,就是傳送資料前必須建立的連線叫三次握手,握手完了才開始發的,這也就是面向連線的意思。

  1. 第一次握手:客戶端傳送syn包(syn=j)到伺服器,並進入SYN_SEND狀態,等待伺服器確認;
  2. 第二次握手:伺服器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也傳送一個SYN包(syn=k),即SYN+ACK包,此時伺服器進入SYN_RECV狀態;
  3. 第三次握手:客戶端收到伺服器的SYN+ACK包,向伺服器傳送確認包ACK(ack=k+1),此包傳送完畢,客戶端和伺服器進入ESTABLISHED狀態,完成三次握手。

3 Java Thread

程式:是一段靜態程式碼,它是應用軟體執行的藍本。

程式:是程式執行時的一個例項,對應了從程式碼載入、執行的一個完整過程,此過程也對應程式產生、發展到消亡的過程。

執行緒:比程式更小的執行單位,一個程式在其執行過程中,可以產生多個執行執行緒,即每個執行緒也有它的產生、發展到消亡的過程。是一個動態概念。

  1. 粒度層次不同,是兩個不同層次上的概念。程式是由作業系統來管理的,而執行緒則是由程式來管理。
  2. 共享資源不同,不同程式的程式碼、內部資料和狀態都是完全獨立的,而一個程式內的多執行緒是共享程式空間資源的,有可能互相影響。
  3. 切換效率不同,執行緒本身的資料通常只有暫存器資料,以及一個程式執行時使用的堆疊,所以執行緒比程式切換負擔小。

3.1 Thread生命週期及狀態模型

執行緒生命週期

3.2 Thread 常用方法

  1. Thread:

public static Thread currentThread( ):返回當前執行緒物件,是一個靜態的方法。

public static void sleep( long millis):使當前執行緒進入睡眠狀態,引數設定其等待時間,不會釋放鎖,靜態方法。

public static void yield():使當前執行緒放棄執行,切換到其它執行緒,是一個靜態方法。

  1. Thread Instance:

public void start():啟動執行緒,JVM將呼叫此執行緒的run方法,結果是將同時執行兩個執行緒,當前執行緒和執行run方法的執行緒。

public void run():Thread的子類應該重寫此方法,內容應為該執行緒應執行的任務。

public void stop():停止執行緒執行,並退出可執行狀態。 【已過時】

public void resume():將暫停的執行緒繼續執行。【已過時】

public void suspend():使執行緒暫停執行,不退出可執行態。【已過時】

public void interrupt():中斷執行緒。

public void join():在當前執行緒中加入呼叫join方法的執行緒A,直到執行緒A死亡才能繼續執行當前執行緒。

public void join(long millis):在當前執行緒中加入呼叫join方法的執行緒A,直到到達引數指定的毫秒數或執行緒A死亡才能繼續執行當前執行緒。

public void setPriority(int newPriority):設定執行緒優先順序。

public void setDaemon(boolean on):設定是否為後臺執行緒。如果當前執行執行緒均為後臺執行緒則JVM停止執行。該方法必須在start()方法之前使用。

public final void checkAccess():判斷當前執行緒是否有權力修改呼叫此方法的執行緒。

public boolean isAlive():判斷執行緒是否處於執行狀態。返回值true表示處於執行狀態,false表示已停止。

  1. Object:

public void wait():在其他執行緒呼叫此物件的notify()方法或notifyAll() 方法前,使當前執行緒進入等待狀態,會釋放鎖。

public void notify(): 喚醒在此物件監視器上等待的單個執行緒。

public void notifyAll():喚醒在此物件監視器上等待的所有執行緒。

3.3 main() 主執行緒與子執行緒

  1. 主執行緒:Java應用程式總是從主類的main()方法開始執行。當JVM載入程式碼,發現main方法之後,啟動的執行緒稱作“主執行緒”,該執行緒負責執行main方法。
  2. 子執行緒:在main方法的執行中再建立的執行緒。

如果main方法中又建立了子執行緒,那麼JVM就要在主執行緒和子執行緒之間輪流切換,main方法即使執行完最後的語句,JVM也不會結束程式,JVM一直要等到程式中的所有執行緒都結束之後,才結束我們的Java應用程式

3.4 Thread 執行緒建立兩種方法

  1. Thread類:Thread類的子類建立執行緒物件,子類重寫Thread類中的run()方法

    在Java程式中建立多執行緒的方法之一是繼承Thread類,從Thread類派生一個子類,並建立這個子類的物件,就可以產生一個新的執行緒。這個子類應該重寫Thread類的run方法,在run方法中寫入需要在新執行緒中執行的語句段。這個子類的物件需要呼叫start方法來啟動,新執行緒等待CPU排程將自動進入run方法。當前執行緒將同時繼續往下執行。

    當建立子類的多個執行緒物件時,其成員變數和run()方法中區域性變數都是互不干擾的

  2. Runnable介面:使用Thread類直接建立執行緒物件,但需要傳入實現Runable介面的目標物件

    在編寫複雜程式時相關的類可能已經繼承了某個基類,而Java不支援多繼承,在這種情況下,便需要通過實現Runnable介面來生成多執行緒。

    使用Thread建立執行緒物件時,通常使用的構造方法是:Thread(Runnable target),該構造方法中的引數是一個Runnable型別的介面,因此,在建立執行緒物件時必須向構造方法的引數傳遞一個實現Runnable介面類的例項,該例項物件稱作所創執行緒的目標物件。

    當執行緒呼叫start方法後,一旦輪到它來享用CPU資源,目標物件就會自動呼叫介面中的run方法(介面回撥)。

    對於使用同一目標物件的執行緒,目標物件的成員變數自然就是這些執行緒的共享資料單元。不同run()方法中的區域性變數互不干擾

3.5 Thread 同步

synchronized —— 執行緒同步關鍵字

把需要修改資料的方法用關鍵字synchronized來修飾,用於指定需要同步的程式碼段或方法,也就是監視區。

當一個執行緒A使用一個synchronized修飾的方法時,其他執行緒想使用這個方法時就必須等待,直到執行緒A 使用完該方法 (除非執行緒A使用wait主動讓出CUP資源)。

當被synchronized限定的程式碼段執行完,就釋放物件鎖(訊號量)。

3.6 Thread 通訊

為了更有效地協調不同執行緒的工作,需要線上程間建立溝通渠道,通過執行緒間的“對話”來解決執行緒間的同步問題。

java.lang.Object 類的一些方法為執行緒間的通訊提供了有效手段。

一個執行緒在使用的同步方法中時,可能根據問題的需要,必須使用wait() (掛起)方法使本執行緒等待,暫時讓出CPU的使用權,並允許其它執行緒使用這個同步方法。其它執行緒如果在使用這個同步方法時如果不需要等待,那麼它用完這個同步方法的同時,應當執行notify(), notifyAll()(恢復)方法通知所有的由於使用這個同步方法而處於等待的執行緒結束等待

3.7 Thread 排程機制

每個Java執行緒都有一個優先順序,其範圍都在1和10之間。 預設情況下,每個執行緒的優先順序都設定為5

線上程A執行過程中建立的新的執行緒物件B,初始狀態具有和執行緒A相同的優先順序。可線上程建立之後的任何時候,通過setPriority(int priority)方法改變其原來的優先順序。

基於執行緒優先順序的執行緒排程:

  1. 具有較高優先順序的執行緒比優先順序較低的執行緒優先執行;
  2. 對具有相同優先順序的執行緒,Java的處理是隨機的;
  3. 底層作業系統支援的優先順序可能要少於10個,這樣會造成一些混亂。因此,只能將優先順序作為一種很粗略的工具使用。最後的控制可以通過明智地使用yield()函式來完成;

假設某執行緒正在執行,則只有出現以下情況之一,才會使其暫停執行:

  1. 一個具有更高優先順序的執行緒變為就緒狀態(Ready);
  2. 由於輸入/輸出(或其他一些原因)、呼叫sleep、wait、yield方法使其發生阻塞;
  3. 對於支援時間分片的系統,時間片的時間期滿;

通常,我們在一個執行緒內部插入yield()語句,這個方法會使正在執行的執行緒暫時放棄執行,這時具有同樣優先順序的執行緒就有機會獲得排程開始執行,但較低優先順序的執行緒仍將被忽略不參加排程;

3.8 Thread 掛起與恢復

掛起 有時候兩個執行緒並不是同步的,即不涉及都需要呼叫一個同步方法,但執行緒也可能需要暫時的掛起。所謂掛起一個執行緒就是讓執行緒暫時讓出CPU的使用許可權,暫時停止執行,但停止執行的持續時間不確定,因此不能使用sleep方法暫停執行緒。掛起一個執行緒需使用wait方法,即讓準備掛起的執行緒呼叫 wait 方法,主動讓出CPU的使用許可權

恢復 為了恢復該執行緒,其它執行緒在佔有CUP資源期間,讓掛起的執行緒的目標物件執行notifyAll()方法,使得掛起的執行緒繼續執行;如果執行緒沒有目標物件,為了恢復該執行緒,其它執行緒在佔有CPU資源期間,讓掛起的執行緒呼叫notifyAll()方法,使掛起的執行緒繼續執行。

4 Java資料結構

  1. 線性表結構一般分為三種:順序線性表、單連結串列、雙連結串列;特徵:

    1. 一個特定的線性表,應該是用來存放特定的某一個型別的元素的( 元素的“同一性”);
    2. 除第一個元素外,其他每一個元素 有且僅有一個直接前驅;除最後一個元素外,其他每一個元素 有且僅有一個直接後繼元素的“序偶性”);
    3. 元素線上性表中的“下標” 唯一地確定該元素在表中的相對位置( 元素的“索引性”)
  2. 二叉樹是樹形結構的一個重要型別。許多實際問題抽象出來的資料結構往往是二叉樹的形式,即使是一般的樹也能簡單地轉換為二叉樹,而且二叉樹的儲存結構及其演算法都較為簡單,因此二叉樹顯得特別重要。

    二叉樹(BinaryTree)是n(n≥0)個結點的有限集,它或者是空集(n=0),或者由一個根結點及兩棵互不相交的、分別稱作這個根的左子樹和右子樹的二叉樹組成。這個定義是遞迴的。由於左、右子樹也是二叉樹, 因此子樹也可為空樹。

    二叉樹遍歷:由於被訪問的結點必是某子樹的根,所以N(Node)、L(Left subtlee)和R(Right subtree)又可解釋為根、根的左子樹和根的右子樹。NLR、LNR和LRN分別又稱為 先根遍歷、中根遍歷和後根遍歷

  3. 佇列是一個常用的資料結構,是一種 先進先出(First In First Out, FIFO) 的結構,也就是說只能在表頭進行刪除,在表尾進行新增。

  4. 棧是一種 後進先出(Last In First Out,LIFO) 的資料結構。

5 JDK命令列工具

jps : 只用於列出java的程式。

jstat :可用於觀察Java應用程式執行時資訊的工具。

jinfo :可用於檢視正在執行的Java應用程式的擴充套件引數,甚至支援在執行時修改部分引數。

jmap :生成Java應用程式的堆快照和物件的統計資訊。jmap -histo 2972 > heap.log

jhat :用於分析Java應用程式的堆快照內容檔案。jhat heap.log

jstack : 用於匯出Java應用程式的執行緒堆疊。 jstack -l 2348 > jstack.log

6 靜態代理和動態代理

  1. 靜態代理:由程式設計師建立或工具生成代理類的原始碼,再編譯代理類。所謂靜態也就是在程式執行前就已經存在代理類的位元組碼檔案,代理類和委託類的關係在編譯期就確定了

  2. 動態代理:動態代理類的原始碼是在程式執行期間由JVM根據反射等機制動態的生成,所以不存在代理類的位元組碼檔案。代理類和委託類的關係是在程式執行期確定

7 Http基礎

7.1 HTTP協議

一種通訊協議,它允許將超文字標記語言(HTML)文件從Web伺服器傳送到客戶端的瀏覽器。 一個屬於應用層的物件導向的協議,由於其簡捷、快速的方式,適用於分散式超媒體資訊系統。

HTTP協議的主要特點如下:

  1. 支援客戶/伺服器模式,基於TCP實現;
  2. 簡單快速:客戶向伺服器請求服務時,只需傳送請求方法和路徑。請求方法常用的有GET、POST、DELETE、PUT。每種方法規定了客戶與伺服器聯絡的型別不同。由於HTTP協議簡單,使得HTTP伺服器的程式規模小,因而通訊速度很快;
  3. 靈活:HTTP允許傳輸任意型別的資料物件。正在 傳輸的型別由 Content-Type 加以標記
  4. 短連線:短連線的含義是限制每次連線只處理一個請求。伺服器處理完客戶的請求,並收到客戶的應答後,即斷開連線。採用這種方式可以節省傳輸時間。
  5. 無狀態:HTTP協議是無狀態協議。無狀態是指協議對於事務處理沒有記憶能力。缺少狀態意味著如果後續處理需要前面的資訊,則它必須重傳,這樣可能導致每次連線傳送的資料量增大。另一方面,在伺服器不需要先前資訊時它的應答就較快。

7.2 CGI是什麼

CGI(Common Gateway Interface),通用閘道器介面,它是一段程式,執行在伺服器上如:HTTP伺服器,提供同客戶端HTML頁面的介面。利用程式的標準輸入輸出流,完成HTTP通訊。HTTP 是文字協議,每次請求的文字以標準輸入流的形式進入伺服器端 CGI 程式,建立程式;然後程式的標準輸出流作為響應

CGI程式可以是Python指令碼,PERL指令碼,SHELL指令碼,C或者C++程式等。 所有伺服器端 HTTP 處理都是類 CGI 的原理,接受符合協議的標準輸入文字流,從標準輸出流輸出同樣符合協議的文字,也就是“請求”和“響應”

7.3 負載均衡、分散式叢集

在負載均衡的思路下,多臺伺服器為對等方式,每臺伺服器都具有同等的地位,可以單獨對外提供服務而無須其他伺服器的輔助。通過負載分擔技術,將外部傳送來的請求按一定規則分配到對稱結構中的某一臺伺服器上,而接收到請求的伺服器都獨立迴應客戶機的請求。

常用負載均衡技術:反向代理負載均衡 (如Apache+JK2+Tomcat這種組合),使用代理伺服器可以將請求轉發給內部的Web伺服器,讓代理伺服器將請求均勻地轉發給多臺內部Web伺服器之一上,從而達到負載均衡的目的。這種代理方式與普通的代理方式有所不同,標準代理方式是客戶使用代理訪問多個外部Web伺服器,而這種代理方式是多個客戶使用它訪問內部Web伺服器,因此也被稱為反向代理模式。

客戶系統一般採用Apache httpd作為web伺服器,即作為Tomcat的前端處理器,根據具體情況而定,有些情況下是不需要Apache httpd作為 web 伺服器的,如系統展現沒有靜態頁面那就不需要Apache httpd,那時可以直接使用Tomcat作為web伺服器來使用。使用Apache httpd主要是它在處理靜態頁面方面的能力比Tomcat強多了。

會話親和:就是表示來自同會話的所有請求都由相同的 Tomcat 例項來處理,這種情況下,如果Tomcat例項或所執行的伺服器機器失效,也會喪失Servlet的會話資料。即使在叢集系統中執行更多的Tomcat實 例,也永遠不會複製會話資料。這樣是提高叢集效能的一種方案,但不具備有容錯能力了。

會話複製:只配置負載均衡還不行,還要 session 複製,也就是說其中任何一個 tomcat 的新增的 session ,是要同步複製到其它 tomcat , 叢集內的 tomcat 都有相同的 session,使用會話複製,則當一個Tomcat例項宕掉時,由於至少還有另一個Tomcat例項保有一份會話狀態資料,因而資料不會喪失。但效能會 有所降低。

其實無論是分散式,資料快取,還是負載均衡,無非就是改善網站的效能瓶頸,在網站原始碼不做優化的情況下,負載均衡可以說是最直接的手段了。其實拋開這個名詞,放開了說,就是希望使用者能夠分流,也就是說把所有使用者的訪問壓力分散到多臺伺服器上,也可以分散到多個 tomcat裡,如果一臺伺服器裝多個tomcat,那麼即使是負載均衡,效能也提高不了太多,不過可以提高穩定性,即容錯性。當其中一個主tomcat 當掉,其他的tomcat也可以補上,因為tomcat之間實現了Session共享。待tomcat伺服器修復後再次啟動,就會自動拷貝所有 session資料,然後加入叢集。這樣就可以不間斷的提供服務。如果要真正從本質上提升效能,必須要分佈到多臺伺服器。

8 Linux基礎

8.1 Linux啟動原理

CMOS:記錄 各項硬體引數且嵌入在主機板上面的儲存器; BIOS:一個寫入到主機板上的一個韌體(韌體就是寫入到硬體上的一個程式)。BIOS就是在開機的時候計算機系統會主動執行的第一個程式了。

  1. 首先載入BIOS程式,通過BIOS程式去 載入CMOS資訊,取得主機的各項硬體配置,並開始進行開機自檢,依據設定取得第一個可啟動的裝置
  2. 載入執行第一個啟動裝置內MBR的Boot Loader
  3. 依據Boot Loader的 設定載入Kernel,Kernel會開始檢測硬體與載入驅動程式;
  4. 在硬體驅動成功後, Kernel會主動呼叫init程式,而init會取得run-level資訊;
  5. init執行 /etc/rc.d/rc.sysinit檔案來準備軟體執行的操作環境(如網路,時區等);
  6. init執行 run-level的各個服務啟動(script方式);
  7. init執行 /etc/rc.d/rc.local檔案;
  8. init執行 終端模擬程式mingetty來啟動login程式,最後就等待使用者登入;

8.2 Linux命令列工具

top實時顯示系統中各個程式的資源佔用狀況。前半部分是系統統計資訊,後半部分是程式佔用資源狀況資訊。

sar週期性地對記憶體和CPU使用情況進行取樣。檢視CPU使用情況:sar -u 1 3;檢視記憶體使用情況:sar -r 1 3;檢視I/O使用情況:sar -b 1 3

vmstat : 週期性地統計CPU 記憶體 swap 使用情況等資訊。vmstat 1 3

iostat : 週期性檢視I/O資訊。iostat 1 2

pidstat:功能強大的效能檢測工具, 不僅可以監視程式的CPU,I/O,記憶體資源,也可以監視執行緒的效能情況

  1. 檢視CPU使用情況: pidstat -p 1187 -u 1 3【-t 引數將系統效能的監控細化到執行緒級別】
  2. 檢視記憶體使用情況: pidstat -p 1187 -r 1 3 【-t 引數將系統效能的監控細化到執行緒級別】
  3. 檢視I/O使用情況: pidstat -p 1187 -d 1 3 【-t 引數將系統效能的監控細化到執行緒級別】

netstat用於檢視各種網路相關資訊,如網路連線,路由表,介面狀態 (Interface Statistics),masquerade 連線,多播成員 (Multicast Memberships) 等等。

netstat -na 來顯示所有連線的埠並用數字表示

mpstat用於獲取 CPU 相關統計資訊。mpstat -P ALL 5 2 顯示了系統中 CPU 的各種統計資訊。–P ALL 選項指示該命令顯示所有 CPU 的統計資訊。引數 5 2 指示該命令每隔 5 秒執行一次,共執行 2 次。

pmap:report memory map of a process(檢視程式的記憶體映像資訊)。

pmap -d 1

 -x   extended       Show the extended format. 顯示擴充套件格式

 -d   device         Show the device format.   顯示裝置格式

 -q   quiet          Do not display some header/footer lines. 不顯示頭尾行

 -V   show version   Displays version of program. 顯示版本複製程式碼

相關文章