筆試題總結

筆試題總結
選擇題
1 一臺機器對200個單詞進行排序花了200秒(使用冒泡排序),那麼花費800秒,大概可以對多少個單詞進行排序
400
500
600
700
解析:冒泡排序的時間複雜度爲O(N^2),
2 如果N2=200,當M2=800時,可以得到M/N=2,因此當N==200,M=400
如欲將B類IP地址168.195.0.0劃分成27個子網。那麼子網掩碼地址是求詳細解()
應該是255.255.248.0,子網掩碼展開成2進制,1的部分爲網絡號,0的部分爲地址,255.255.248.0展開後爲11111111.11111111.11111000.0000000;由於該網絡屬於B類,則前兩個255可以暫且不管他,只看248這個地方,表示網絡的1一共有5個,也就是劃分爲了25=32個網絡,滿足27個的劃分。如果是224的話,那個1有3個,只能劃分成23=8個網絡,不滿足條件。
3、關於進程和線程,不正確的描述是(D)
A 進程的隔離性要好於線程
B 線程在資源消耗上通常要比進程輕量
C 不同進程間不會共享邏輯地址空間
D 同一個進程的線程之間共享內存,包括堆和棧。
E 進程間有途徑共享大量內存中的數據
F 線程間通訊可以通過直接訪問全局變量,或者使用進程間通信的機制IPC

解析:選D因爲堆可以共享但是棧不可以共享

4、找規律填數字:2,2,2,4,12,(),480,5760
A 48
B 60
C 64
D 120

規律如下:
鏈接:https://www.nowcoder.com/questionTerminal/0f753906814c4e8386145209df65cef2?toCommentId=1646270
來源:牛客網

2 2 2 4 12 () 480 5760
2* 1 = 2
2 * 1 = 2
2 * (2) = 4 (2) = 1 + 1
4 * (3) = 12 (3) = 2 + 1
12 * (5) = 60 (5) = 2 + 3
60 * (8) = 480 (8) = 5+ 3
480 * (13) = 5760 (13) = 5+ 8
5、併發操作會帶來哪些數據的不一致性(A)
A 丟失修改 髒讀、死鎖
B 不可重複讀 髒讀 死鎖
C 不可修改 不可重複讀 髒讀 死鎖
D 丟失修改 不可重複讀 髒讀

解析:併發操作帶來的問題:

(1)丟失修改

(2)髒讀

(3)不可重複讀

網頁出現400錯誤主要有兩種原因,一個是請求的域名不存在,另一個是請求錯誤。出現這種情況時,可以用以下幾種方法解決。
簡答題:
進程、線程和協程的理解
進程擁有自己獨立的堆和棧,既不共享堆,亦不共享棧,進程由操作系統調度。
線程擁有自己獨立的棧和共享的堆,共享堆,不共享棧,線程亦由操作系統調度(標準線程是的)。
協程和線程一樣共享堆,不共享棧,協程由程序員在協程的代碼裏顯示調度。
進程和其他兩個的區別還是很明顯的。
協程和線程的區別是:協程避免了無意義的調度,由此可以提高性能,但也因此,程序員必須自己承擔調度的責任,同時,協程也失去了標準線程使用多CPU的能力。
打個比方吧,假設有一個操作系統,是單核的,系統上沒有其他的程序需要運行,有兩個線程 A 和 B ,A 和 B 在單獨運行時都需要 10 秒來完成自己的任務,而且任務都是運算操作,A B 之間也沒有競爭和共享數據的問題。現在 A B 兩個線程並行,操作系統會不停的在 A B 兩個線程之間切換,達到一種僞並行的效果,假設切換的頻率是每秒一次,切換的成本是 0.1 秒(主要是棧切換),總共需要 20 + 19 * 0.1 = 21.9 秒。如果使用協程的方式,可以先運行協程 A ,A 結束的時候讓位給協程 B ,只發生一次切換,總時間是 20 + 1 * 0.1 = 20.1 秒。如果系統是雙核的,而且線程是標準線程,那麼 A B 兩個線程就可以真並行,總時間只需要 10 秒,而協程的方案仍然需要 20.1 秒。

進程相互通信

  1. 管道( pipe ):管道是一種半雙工的通信方式,數據只能單向流動,而且只能在具有親緣關係的進程間使用。進程的親緣關係通常是指父子進程關係。
  2. 有名管道 (named pipe) : 有名管道也是半雙工的通信方式,但是它允許無親緣關係進程間的通信。
  3. 信號量( semophore ) : 信號量是一個計數器,可以用來控制多個進程對共享資源的訪問。它常作爲一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。因此,主要作爲進程間以及同一進程內不同線程之間的同步手段。
  4. 消息隊列( message queue ) : 消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識。消息隊列克服了信號傳遞信息少、管道只能承載無格式字節流以及緩衝區大小受限等缺點。
  5. 信號 ( sinal ) : 信號是一種比較複雜的通信方式,用於通知接收進程某個事件已經發生。
  6. 共享內存( shared memory ) :共享內存就是映射一段能被其他進程所訪問的內存,這段共享內存由一個進程創建,但多個進程都可以訪問。共享內存是最快的 IPC 方式,它是針對其他進程間通信方式運行效率低而專門設計的。它往往與其他通信機制,如信號兩,配合使用,來實現進程間的同步和通信。eg:golang channel
  7. 套接字( socket ) : 套解口也是一種進程間通信機制,與其他通信機制不同的是,它可用於不同及其間的進程通信。

線程相互通信

鎖機制:包括互斥鎖、條件變量、讀寫鎖
*互斥鎖提供了以排他方式防止數據結構被併發修改的方法。
*讀寫鎖允許多個線程同時讀共享數據,而對寫操作是互斥的。
*條件變量可以以原子的方式阻塞進程,直到某個特定條件爲真爲止。對條件的測試是在互斥鎖的保護下進行的。條件變量始終與互斥鎖一起使用。
信號量機制(Semaphore):包括無名線程信號量和命名線程信號量
信號機制(Signal):類似進程間的信號處理
全局變量或者某個對象

協程

進程擁有自己獨立的堆和棧,既不共享堆,亦不共享棧,進程由操作系統調度。 線程擁有自己獨立的棧和共享的堆,共享堆,不共享棧,線程亦由操作系統調度(標準線程是的)。
協程和線程一樣共享堆,不共享棧,協程由程序員在協程的代碼裏顯示調度。
協程和線程的區別是:協程避免了無意義的調度,由此可以提高性能,但也因此,程序員必須自己承擔調度的責任,同時,協程也失去了標準線程使用多CPU的能力。

2的1次方個位數2;2的2次方個位數是4;2的3次方個位數是8;2的4次方個位數是6;2的5次方個位數是2……(n次方的個位數就是n-1次方的個位數與2的乘積的個位數)
得到2的(1+4n)次方的個位數是2;2的(2+4n)次方的個位數是4;2的(3+4n)次方的個位數是8;2的(4+4n)次方的個位數是6(其中n=0,1,2,3……)
64=4+4*15
所以2的64次方的個位數是6

一、靜態web頁面:
1、在靜態Web程序中,客戶端使用Web瀏覽器(IE、FireFox等)經過網絡(Network)連接到服務器上,使用HTTP協議發起一個請求(Request),告訴服務器我現在需要得到哪個頁面,所有的請求交給Web服務器,之後WEB服務器根據用戶的需要,從文件系統(存放了所有靜態頁面的磁盤)取出內容。之後通過Web服務器返回給客戶端,客戶端接收到內容之後經過瀏覽器渲染解析,得到顯示的效果。
2、爲了讓靜態web頁面顯示更加好看,使用javascript/VBScript/ajax(AJAX即“Asynchronous Javascript And XML”(異步JavaScript和XML),是指一種創建交互式網頁應用的網頁開發技術。)但是這些特效都是在客戶端上藉助於瀏覽器展現給用戶的,所以在服務器上本身並沒有任何的變化。
3、靜態web無法連接數據庫;
4、靜態web資源開發技術:HTML;
5、由於現在的web頁面中,大量使用JS,導致瀏覽器打開頁面,就會佔用大量的內存,服務端的壓力是減輕了,但壓力轉移到了客戶端。

二、動態web頁面:
動態WEB中,程序依然使用客戶端和服務端,客戶端依然使用瀏覽器(IE、FireFox等),通過網絡(Network)連接到服務器上,使用HTTP協議發起請求(Request),現在的所有請求都先經過一個WEB Server來處理。
如果客戶端請求的是靜態資源(.htm或者是.htm),則將請求直接轉交給WEB服務器,之後WEB服務器從文件系統中取出內容,發送回客戶端瀏覽器進行解析執行。

如果客戶端請求的是動態資源(.jsp、.asp/.aspx、.php),則先將請求轉交給WEB Container(WEB容器),在WEB Container中連接數據庫,從數據庫中取出數據等一系列操作後動態拼湊頁面的展示內容,拼湊頁面的展示內容後,把所有的展示內容交給WEB服務器,之後通過WEB服務器將內容發送回客戶端瀏覽器進行解析執行。
再進一步深入分析動態web的訪問過程:瀏覽器訪問web時,看似是直接訪問的jsp頁面,其實是,最先到達的地方是服務器,服務器創建好req和resp對象後再給jsp頁面使用。在jsp中完成設置字符集和取得表單參數後再調用servlet,完成業務處理。然後返回到jsp,jsp就會生成相應的html頁面。該頁面會返回到服務器,再由服務器,通過response對象返回給客戶端。
爲什麼需要web服務器?(web server)
1)不管什麼web資源,想被遠程計算機訪問,都必須有一個與之對應的網絡通信程序,當用戶來訪問時,這個網絡通信程序讀取web資源數據,並把數據發送給來訪者。
2)WEB服務器就是這樣一個程序,它用於完成底層網絡通迅,處理http協議。使用這些服務器,We應用的開發者只需要關注web資源怎麼編寫,而不需要關心資源如何發送到客戶端手中,從而極大的減輕了開發者的開發工作量。

常用動態web資源開發技術:JSP/Servlet、ASP、PHP等。

3、線程運行時幾種狀態的轉換過程?

線程在一定條件下,狀態會發生變化。線程一共有以下幾種狀態:

**1、新建狀態(New):**新創建了一個線程對象。

**2、就緒狀態(Runnable):**線程對象創建後,其他線程調用了該對象的start()方法。該狀態的線程位於“可運行線程池”中,變得可運行,只等待獲取CPU的使用權。即在就緒狀態的進程除CPU之外,其它的運行所需資源都已全部獲得。

**3、運行狀態(Running):**就緒狀態的線程獲取了CPU,執行程序代碼。

**4、阻塞狀態(Blocked):**阻塞狀態是線程因爲某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,纔有機會轉到運行狀態。

阻塞的情況分三種:

(1)、**等待阻塞:**運行的線程執行wait()方法,該線程會釋放佔用的所有資源,JVM會把該線程放入“等待池”中。進入這個狀態後,是不能自動喚醒的,必須依靠其他線程調用notify()或notifyAll()方法才能被喚醒,

(2)、**同步阻塞:**運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入“鎖池”中。

(3)、**其他阻塞:**運行的線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。

**5、死亡狀態(Dead):**線程執行完了或者因異常退出了run()方法,該線程結束生命週期。

線程變化的狀態轉換圖如下:
在這裏插入圖片描述

注:拿到對象的鎖標記,即爲獲得了對該對象(臨界區)的使用權限。即該線程獲得了運行所需的資源,進入“就緒狀態”,只需獲得CPU,就可以運行。因爲當調用wait()後,線程會釋放掉它所佔有的“鎖標誌”,所以線程只有在此獲取資源才能進入就緒狀態。
下面小小的作下解釋:
1、線程的實現有兩種方式,一是繼承Thread類,二是實現Runnable接口,但不管怎樣, 當我們new了這個對象後,線程就進入了初始狀態;
2、當該對象調用了start()方法,就進入就緒狀態;
3、進入就緒後,當該對象被操作系統選中,獲得CPU時間片就會進入運行狀態;
4、進入運行狀態後情況就比較複雜了
4.1、run()方法或main()方法結束後,線程就進入終止狀態;
4.2、當線程調用了自身的sleep()方法或其他線程的join()方法,進程讓出CPU,然後就會進入阻塞狀態(該狀態既停止當前線程,但並不釋放所佔有的資源即調用sleep ()函數後,線程不會釋放它的“鎖標誌”。)。當sleep()結束或join()結束後,該線程進入可運行狀態,繼續等待OS分配CPU時間片。典型地,sleep()被用在等待某個資源就緒的情形:測試發現條件不滿足後,讓線程阻塞一段時間後重新測試,直到條件滿足爲止。
4.3、線程調用了yield()方法,意思是放棄當前獲得的CPU時間片,回到就緒狀態,這時與其他進程處於同等競爭狀態,OS有可能會接着又讓這個進程進入運行狀態; 調用 yield() 的效果等價於調度程序認爲該線程已執行了足夠的時間片從而需要轉到另一個線程。yield()只是使當前線程重新回到可執行狀態,所以執行yield()的線程有可能在進入到可執行狀態後馬上又被執行。
4.4、當線程剛進入可運行狀態(注意,還沒運行),發現將要調用的資源被synchroniza(同步),獲取不到鎖標記,將會立即進入鎖池狀態,等待獲取鎖標記(這時的鎖池裏也許已經有了其他線程在等待獲取鎖標記,這時它們處於隊列狀態,既先到先得),一旦線程獲得鎖標記後,就轉入就緒狀態,等待OS分配CPU時間片;

4.5. suspend() 和 resume()方法:兩個方法配套使用,suspend()使得線程進入阻塞狀態,並且不會自動恢復,必須其對應的resume()被調用,才能使得線程重新進入可執行狀態。典型地,suspend()和 resume() 被用在等待另一個線程產生的結果的情形:測試發現結果還沒有產生後,讓線程阻塞,另一個線程產生了結果後,調用 resume()使其恢復。
4.6、wait()和 notify() 方法:當線程調用wait()方法後會進入等待隊列(進入這個狀態會釋放所佔有的所有資源,與阻塞狀態不同),進入這個狀態後,是不能自動喚醒的,必須依靠其他線程調用notify()或notifyAll()方法才能被喚醒(由於notify()只是喚醒一個線程,但我們由不能確定具體喚醒的是哪一個線程,也許我們需要喚醒的線程不能夠被喚醒,因此在實際使用時,一般都用notifyAll()方法,喚醒有所線程),線程被喚醒後會進入鎖池,等待獲取鎖標記。

wait() 使得線程進入阻塞狀態,它有兩種形式:

一種允許指定以毫秒爲單位的一段時間作爲參數;另一種沒有參數。前者當對應的 notify()被調用或者超出指定時間時線程重新進入可執行狀態即就緒狀態,後者則必須對應的 notify()被調用。當調用wait()後,線程會釋放掉它所佔有的“鎖標誌”,從而使線程所在對象中的其它synchronized數據可被別的線程使用。waite()和notify()因爲會對對象的“鎖標誌”進行操作,所以它們必須在synchronized函數或synchronizedblock中進行調用。如果在non-synchronized函數或non-synchronizedblock中進行調用,雖然能編譯通過,但在運行時會發生IllegalMonitorStateException的異常。

注意區別:初看起來wait() 和 notify() 方法與suspend()和 resume() 方法對沒有什麼分別,但是事實上它們是截然不同的。區別的核心在於,前面敘述的suspend()及其它所有方法在線程阻塞時都不會釋放佔用的鎖(如果佔用了的話),而wait() 和 notify() 這一對方法則相反。

上述的核心區別導致了一系列的細節上的區別

首先,前面敘述的所有方法都隸屬於 Thread類,但是wait() 和 notify() 方法這一對卻直接隸屬於 Object 類,也就是說,所有對象都擁有這一對方法。初看起來這十分不可思議,但是實際上卻是很自然的,因爲這一對方法阻塞時要釋放佔用的鎖,而鎖是任何對象都具有的,調用任意對象的 wait() 方法導致線程阻塞,並且該對象上的鎖被釋放。而調用任意對象的notify()方法則導致因調用該對象的 wait()方法而阻塞的線程中隨機選擇的一個解除阻塞(但要等到獲得鎖後才真正可執行)。

其次,前面敘述的所有方法都可在任何位置調用,但是wait() 和 notify() 方法這一對方法卻必須在 synchronized 方法或塊中調用,理由也很簡單,只有在synchronized方法或塊中當前線程才佔有鎖,纔有鎖可以釋放。同樣的道理,調用這一對方法的對象上的鎖必須爲當前線程所擁有,這樣纔有鎖可以釋放。因此,這一對方法調用必須放置在這樣的 synchronized方法或塊中,該方法或塊的上鎖對象就是調用這一對方法的對象。若不滿足這一條件,則程序雖然仍能編譯,但在運行時會出現IllegalMonitorStateException異常。

wait() 和 notify()方法的上述特性決定了它們經常和synchronized方法或塊一起使用,將它們和操作系統的進程間通信機制作一個比較就會發現它們的相似性:synchronized方法或塊提供了類似於操作系統原語的功能,它們的執行不會受到多線程機制的干擾,而這一對方法則相當於block和wake up 原語(這一對方法均聲明爲 synchronized)。它們的結合使得我們可以實現操作系統上一系列精妙的進程間通信的算法(如信號量算法),並用於解決各種複雜的線程間通信問題。

關於 wait() 和 notify() 方法最後再說明兩點:

第一:調用notify() 方法導致解除阻塞的線程是從因調用該對象的 wait()方法而阻塞的線程中隨機選取的,我們無法預料哪一個線程將會被選擇,所以編程時要特別小心,避免因這種不確定性而產生問題。

第二:除了notify(),還有一個方法 notifyAll()也可起到類似作用,唯一的區別在於,調用 notifyAll()方法將把因調用該對象的 wait()方法而阻塞的所有線程一次性全部解除阻塞。當然,只有獲得鎖的那一個線程才能進入可執行狀態。

談到阻塞,就不能不談一談死鎖,略一分析就能發現,suspend()方法和不指定超時期限的wait()方法的調用都可能產生死鎖。遺憾的是,Java並不在語言級別上支持死鎖的避免,我們在編程中必須小心地避免死鎖。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章