1、介紹一下數據庫的三大范式
第一范式(1NF):屬性不可分割,即每個屬性都是不可分割的原子項。(實體的屬性即表中的列)
第二范式(2NF):滿足第一范式;且不存在部分依賴,即非主屬性必須完全依賴于主屬性。(主屬性即主鍵;完全依賴是針對于聯合主鍵的情況,非主鍵列不能只依賴于主鍵的一部分)
第三范式(3NF):滿足第二范式;且不存在傳遞依賴,即非主屬性不能與非主屬性之間有依賴關系,非主屬性必須直接依賴于主屬性,不能間接依賴主屬性。(A -> B, B ->C, A -> C)
2、介紹一下數據庫的隔離級別
事務的特性(ACID):
原子性(Atomicity):原子性是指一個事務中的操作,要么全部成功,要么全部失敗,如果失敗,就回滾到事務開始前的狀態。
一致性(Consistency):一致性是指事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之后都必須處于一致性狀態。那轉賬舉栗子,A賬戶和B賬戶之間相互轉賬,無論如何操作,A、B賬戶的總金額都必須是不變的。
隔離性(Isolation):隔離性是當多個用戶并發的訪問數據庫時,如果操作同一張表,數據庫則為每一個用戶都開啟一個事務,且事務之間互不干擾,也就是說事務之間的并發是隔離的。再舉個栗子,現有兩個并發的事務T1和T2,T1要么在T2開始前執行,要么在T2結束后執行,如果T1先執行,那T2就在T1結束后在執行。關于數據的隔離性級別,將在后文講到。
持久性(Durability):持久性就是指如果事務一旦被提交,數據庫中數據的改變就是永久性的,即使斷電或者宕機的情況下,也不會丟失提交的事務操作。
事務的隔離性(Isolation)是指,多個用戶的并發事務訪問同一個數據庫時,一個用戶的事務不應該被其他用戶的事務干擾,多個并發事務之間要相互隔離。
如果不考慮隔離性,會發生什么事呢?
1.臟讀:
臟讀是指一個事務在處理數據的過程中,讀取到另一個為提交事務的數據。
2.不可重復讀:
不可重復讀是指對于數據庫中的某個數據,一個事務范圍內的多次查詢卻返回了不同的結果,這是由于在查詢過程中,數據被另外一個事務修改并提交了。
不可重復讀和臟讀的區別是,臟讀讀取到的是一個未提交的數據,而不可重復讀讀取到的是前一個事務提交的數據。
而不可重復讀在一些情況也并不影響數據的正確性,比如需要多次查詢的數據也是要以最后一次查詢到的數據為主。
3.幻讀
幻讀是事務非獨立執行時發生的一種現象。例如事務T1對一個表中所有的行的某個數據項做了從“1”修改為“2”的操作,這時事務T2又對這個表中插入了一行數據項,而這個數據項的數值還是為“1”并且提交給數據庫。而操作事務T1的用戶如果再查看剛剛修改的數據,會發現還有一行沒有修改,其實這行是從事務T2中添加的,就好像產生幻覺一樣,這就是發生了幻讀。
幻讀和不可重復讀都是讀取了另一條已經提交的事務(這點就臟讀不同),所不同的是不可重復讀查詢的都是同一個數據項,而幻讀針對的是一批數據整體(比如數據的個數)。
總的來說,解決不可重復讀的方法是鎖行,解決幻讀的方式是鎖表。
四種隔離級別解決了上述問題。
1.讀未提交(Read uncommitted):
這種事務隔離級別下,select語句不加鎖。
此時,可能讀取到不一致的數據,即“讀臟 ”。這是并發最高,一致性最差的隔離級別。
2.讀已提交(Read committed):
可避免 臟讀 的發生。
在互聯網大數據量,高并發量的場景下,幾乎 不會使用 上述兩種隔離級別。
3.可重復讀(Repeatable read):
MySql默認隔離級別。
可避免 臟讀 、不可重復讀 的發生。
4.串行化(Serializable ):
可避免 臟讀、不可重復讀、幻讀 的發生。
以上四種隔離級別最高的是 Serializable 級別,最低的是 Read uncommitted 級別,當然級別越高,執行效率就越低。像 Serializable 這樣的級別,就是以 鎖表 的方式(類似于Java多線程中的鎖)使得其他的線程只能在鎖外等待,所以平時選用何種隔離級別應該根據實際情況。在MySQL數據庫中默認的隔離級別為Repeatable read (可重復讀) 。
在MySQL數據庫中,支持上面四種隔離級別,默認的為Repeatable read (可重復讀) ;而在 Oracle數據庫 中,只支持Serializable (串行化) 級別和 Read committed (讀已提交) 這兩種級別,其中默認的為 Read committed(讀已提交) 級別。
3、單例模式
1. 什么是單例模式
面試官問什么是單例模式時,千萬不要答非所問,給出單例模式有兩種類型之類的回答,要圍繞單例模式的定義去展開。
單例模式是指在內存中只會創建且僅創建一次對象的設計模式。在程序中多次使用同一個對象且作用相同時,為了防止頻繁地創建對象使得內存飆升,單例模式可以讓程序僅在內存中創建一個對象,讓所有需要調用的地方都共享這一單例對象。
2. 單例模式的類型
單例模式有兩種類型:
懶漢式:在真正需要使用對象時才去創建該單例類對象
餓漢式:在類加載時已經創建好該單例對象,等待被程序使用
懶漢式創建單例對象。
懶漢式創建對象的方法是在程序使用對象前,先判斷該對象是否已經實例化(判空),若已實例化直接返回該類對象,否則則先執行實例化操作。
4、violate 的作用是什么?
使用volatile關鍵字可以防止指令重排序,使用volatile關鍵字修飾的變量,可以保證其指令執行的順序與程序指明的順序一致,不會發生順序變換,這樣在多線程環境下就不會發生NPE異常了。
volatile還有第二個作用:使用volatile關鍵字修飾的變量,可以保證其內存可見性,即每一時刻線程讀取到該變量的值都是內存中最新的那個值,線程每次操作該變量都需要先讀取該變量。
總結:
(1)單例模式常見的寫法有兩種:懶漢式、餓漢式
(2)懶漢式:在需要用到對象時才實例化對象,正確的實現方式是:Double Check + Lock,解決了并發安全和性能低下問題。
(3)餓漢式:在類加載時已經創建好該單例對象,在獲取單例對象時直接返回對象即可,不會存在并發安全和性能問題。
(4)在開發中如果對內存要求非常高,那么使用懶漢式寫法,可以在特定時候才創建該對象;
(5)如果對內存要求不高使用餓漢式寫法,因為簡單不易出錯,且沒有任何并發安全和性能問題。
(6)為了防止多線程環境下,因為指令重排序導致變量報NPE,需要在單例對象上添加volatile關鍵字防止指令重排序。
(7)最優雅的實現方式是使用枚舉,其代碼精簡,沒有線程安全問題,且 Enum 類內部防止反射和反序列化時破壞單例。
5、synchronized 的實現(方法、代碼塊、對象)
synchronized關鍵字解決的是多個線程之間訪問資源的同步性,synchronized 翻譯為中文的意思是同步,也稱之為同步鎖。
synchronized的作用是保證在同一時刻,被修飾的代碼塊或方法只會有一個線程執行,以達到保證并發安全的效果。
特性:
(1)原子性:synchronized保證語句塊內操作是原子的.
(2)可見性:synchronized保證可見性(通過“在執行unlock之前,必須先把此變量同步回主內存”實現)
(3)有序性:synchronized保證有序性(通過“一個變量在同一時刻只允許一條線程對其進行lock操作”)
(4)重入性:synchronized 是可重入鎖,也就是說,允許一個線程二次請求自己持有對象鎖的臨界資源,這種情況稱為可重入鎖。(是因為 synchronized 鎖對象有個計數器,會隨著線程獲取鎖后 +1 計數,當線程執行完畢后 -1,直到清零釋放鎖。)
Synchronized的四種狀態(優化):
(1)無鎖狀態:表示沒有線程占用此鎖。
(2)偏向鎖:一個線程一直訪問此同步代碼,一種占用此鎖,那么該線程就會自動獲取鎖,提高效率。
(3)輕量級鎖:當鎖是偏向鎖時,此時又有其他的線程來搶占,就會升級為輕量級鎖,未搶占到鎖的線程就會通過自旋的形式嘗試去獲取鎖,提高性能。
(4)重量級鎖:當鎖是重量級鎖時,未搶占到鎖的線程自旋到一定次數(對CPU消耗大),還未拿到鎖時,就會升級為重量級鎖,此線程會等待操作系統的調動,就不在主動的去搶占獲取鎖了。
對象結構:
Synchronized鎖的狀態存儲在鎖對象的對象頭里,對象頭中有一塊區域為Mark Word,用于存儲對象運行時的數據,比如:hashcode,GC次數,鎖狀態標識。32位操作系統Mark Word為32位,64位操作系統Mark Word為64位。
鎖的具體實現:
線程代碼進入到Synchronized代碼塊時會自動獲取鎖對象,這時其他線程訪問時會被阻塞,直到Synchroinzed代碼塊執行完畢或拋出異常,調用wait()方法都會釋放鎖對象。在進入Synchronized代碼塊時會將主內存的變量讀取到自己的工作內存,在退出的時候會把工作內存的更新值寫入到主內存。Java中Synchronized通過在鎖對象的對象頭設置標記,達到獲取鎖和釋放鎖的目的。
6. 沖突:對不同的關鍵字可能得到同一哈希地址,即key1≠key2而f(key1)=f(key2)這種現象稱沖突(collision)。具有相同函數值的關鍵詞對該哈希函數來說稱作同義詞(synonym)。
一 、哈希函數
(1)除留余數法(K%P P一般情況下取小于表長的最大素數 表長是100則p取97 若表長是25則p取23)
(2)數字分析法
(3)平方取中法
(4)直接定址法
二 、解決沖突的方法
(1)開放定址法(1.線性探測法 +1 +2 +3… 2.二次探測法 +12 -12 +22 -22)
(2)再哈希法(增加了不算的時間 不容易產生聚集現象)
注意:沖突只能減小 不可以避免
7.Switch case語句、常用情況
switch case 語句:根據表達式的結果,尋找匹配的case,并執行case后面的語句,一直到break為止,如果沒有遇到 break 那就接著執行下面的語句。如果所有的case都不匹配,那么就執行default 后面的語句;如果沒有default,那么程序就什么都不會執行,直接跳過switch case 語句。
注意:
1.switch case 語句的結果只能是整數(int)類型
2.控制表達式只能是整數型的結果
3.常量可以是常數,也可以是常數計算的表達式
4.break 在程序中是代表 “結束” 的意思
8.結構體和共同體的區別:
定義:
結構體struct:把不同類型的數據組合成一個整體,自定義類型。
共同體union:使幾個不同類型的變量共同占用一段內存。
地址:struct和union都有內存對齊,結構體的內存布局依賴于CPU、操作系統、編譯器及編譯時的對齊選項。
結構體struct:不同之處,struct里每個成員都有自己獨立的地址。sizeof(struct)是內存對齊后所有成員長度的加和。
共同體union:當共同體中存入新的數據后,原有的成員就失去了作用,新的數據被寫到union的地址中。sizeof(union)是最長的數據成員的長度。