記2017.3.21阿里面試經歷,java方向

1. Java有什麼新特性

  • Java語言
  • 編譯器
  • 類庫
  • 工具
  • Java運行時

1. 1 Java語言

  • Lambda表達式(閉包)允許把函數作爲一個方法的參數,或者把代碼看成數據。
Arrays.asList("a","b","d").forEach(e->System.out.println(e));
Arrays.asList( "a", "b", "d" ).forEach( e -> {
    System.out.print( e );
    System.out.print( e );
} );
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
  • 增加函數式接口,可以被隱式轉換爲lambda表達式@FunctionInterface,默認方法與靜態方法並不影響函數式接口的契約。
  • 接口的默認方法與靜態方法,所有實現者將會默認繼承它(如果有必要的話,可以覆蓋這個默認實現)
  • 方法引用:構造器引用Class::new(無參),靜態方法引用Class::static_method,特定類的任意對象Class::method,特定對象instance::method
  • 重複註解

1. 2 Java編譯器的新特性

  • 參數名字,方便獲取參數名字

1. 3 Java類庫的新特性

  • Optional 一個容器,可以保存類型T的值,或者僅僅保存null,提供有用的方法,這樣我們就不用顯示進行空值檢。如果Optional類的實例爲非空值的話,isPresent()返回true,否則返回false,爲了防止Optional爲空值,orElseGet()方法通過回調函數來產生一個默認值。map()函數對當前Optional的值進行轉換,然後返回一個新的Optional實例。
  • Stream 真正的函數式變成風格引入到Java中,極大簡化了集合框架的處理。
  • Date/Time API  Clock類,指定時區,獲得當前時刻,日期,時間
  • parrellelSort()方法,在多核機器上極大提高數組排序速度。
Optional< String > fullName = Optional.ofNullable( null );
System.out.println( "Full Name is set? " + fullName.isPresent() );        
System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) ); 
System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );

2. synchronized如何實現

  • 對象頭 Mark
鎖就保存在對象頭中,對象頭分爲兩部分信息,第一部分用於存儲對象自身運行時的數據,如HashCode,GC分代年齡等,另一部分用於存儲指向方法區類型數據的指針。
  • 偏向鎖
偏向鎖實際上是一種優化鎖,其目的是爲了減少數據在無競爭情況下的性能損耗。其核心思想就是鎖會偏向第一個獲取它的線程,在接下來的執行過程中該鎖沒有其他的線程獲取,則持有偏向鎖的線程永遠不需要同步。
  • 輕量級鎖
在沒有多線程競爭的前提下,減少傳統的重量級鎖使用操作系統互斥量產生的性能損耗。
  • 自旋鎖
sychronized鎖是一種重量級鎖,在互斥狀態下,沒有得到鎖的線程會被掛起阻塞,而掛起線程和恢復線程的操作都需要轉入內核態中完成。所謂自旋鎖,就是讓沒有獲得鎖的進程自己運行一段時間自循環(默認開啓),但是不掛起線程的代價就是該線程會一直佔用處理器,如果鎖佔用的時間很短,自旋等待的效果很好,反之,自旋鎖會消耗大量處理器資源,因此,自旋的等待時間必須有一定限度,超過限度還沒有獲得鎖,就要掛起線程。
  • 鎖的升級過程
爲了減少獲得鎖和釋放鎖帶來的性能消耗,這幾個狀態會隨着競爭情況逐漸升級,狀態依次是無狀態鎖、偏向鎖、輕量級鎖、自旋鎖和重量級鎖。鎖只能升級不能降級。
  • synchronized用法:
修飾方法和代碼塊:某個線程得到了對象鎖,但是另一個線程還是可以訪問沒有進行同步的方法或者代碼,進行了同步的方法和沒有進行同步的方法是互不影響的,一個線程進入了同步方法,得到了對象鎖,其他線程還是可以訪問那些沒有同步的方法,兩個被加鎖的對象方法具有互斥性。同時修飾靜態方法和實例方法,卻可以交替進行互不干擾。
  • synchronized的缺陷:
當某個線程進入同步方法獲得對象鎖,那麼其他線程訪問這裏對象的同步方法時,必須等待或者阻塞,對高併發系統是致命的。如果某個線程在同步方法裏面發生了死循環,那麼永遠不會釋放對象鎖,其他線程就要永遠等待。
synchronized塊具有優勢,同步操作的內容可以與同步對象無關。
  • 形象的比喻

打個比方:一個object就像一個大房子,大門永遠打開。房子裏有 很多房間(也就是方法)。

這些房間有上鎖的(synchronized方法), 和不上鎖之分(普通方法)。房門口放着一把鑰匙(key),這把鑰匙可以打開所有上鎖的房間。

另外我把所有想調用該對象方法的線程比喻成想進入這房子某個 房間的人。所有的東西就這麼多了,下面我們看看這些東西之間如何作用的。

在此我們先來明確一下我們的前提條件。該對象至少有一個synchronized方法,否則這個key還有啥意義。當然也就不會有我們的這個主題了。

一個人想進入某間上了鎖的房間,他來到房子門口,看見鑰匙在那兒(說明暫時還沒有其他人要使用上鎖的 房間)。於是他走上去拿到了鑰匙,並且按照自己 的計劃使用那些房間。注意一點,他每次使用完一次上鎖的房間後會馬上把鑰匙還回去。即使他要連續使用兩間上鎖的房間,中間他也要把鑰匙還回去,再取回來。

因此,普通情況下鑰匙的使用原則是:“隨用隨借,用完即還。”

這時其他人可以不受限制的使用那些不上鎖的房間,一個人用一間可以,兩個人用一間也可以,沒限制。但是如果當某個人想要進入上鎖的房間,他就要跑到大門口去看看了。有鑰匙當然拿了就走,沒有的話,就只能等了。

要是很多人在等這把鑰匙,等鑰匙還回來以後,誰會優先得到鑰匙?Not guaranteed。象前面例子裏那個想連續使用兩個上鎖房間的傢伙,他中間還鑰匙的時候如果還有其他人在等鑰匙,那麼沒有任何保證這傢伙能再次拿到。 

再來看看同步代碼塊。和同步方法有小小的不同。

1.從尺寸上講,同步代碼塊比同步方法小。你可以把同步代碼塊看成是沒上鎖房間裏的一塊用帶鎖的屏風隔開的空間。

2.同步代碼塊還可以人爲的指定獲得某個其它對象的key。就像是指定用哪一把鑰匙才能開這個屏風的鎖,你可以用本房的鑰匙;你也可以指定用另一個房子的鑰匙才能開,這樣的話,你要跑到另一棟房子那兒把那個鑰匙拿來,並用那個房子的鑰匙來打開這個房子的帶鎖的屏風。

         記住你獲得的那另一棟房子的鑰匙,並不影響其他人進入那棟房子沒有鎖的房間。

         爲什麼要使用同步代碼塊呢?我想應該是這樣的:首先對程序來講同步的部分很影響運行效率,而一個方法通常是先創建一些局部變量,再對這些變量做一些 操作,如運算,顯示等等;而同步所覆蓋的代碼越多,對效率的影響就越嚴重。因此我們通常儘量縮小其影響範圍。


3. 事務介紹

http://blog.csdn.net/zhanghaor/article/details/57084350

事務的四個屬性:持久性、原子性、隔離性、一致性。其中一致性是最基本的屬性,其他三個屬性都是爲了保證一致性二存在的。所謂一致性是指數據處於一種有意義的狀態,這種狀態是語義上的而不是語法上的。例如轉賬過程中的一致性。
在數據庫實現場景中,一致性可以分爲數據庫外部一致性和數據庫內部一致性。前者由外部應用的編碼來保證,即某個應用在執行轉賬的數據庫操作時,必須在同一個事務內部調用對賬戶A和賬戶B的操作,㐊數據庫本身能解決的。後者有數據庫來保證,即在同一個事務內部的一組操作必須全部執行成功,這是事務處理的原子性。
爲了實現原子性,需要通過日誌,將所有對數據庫的更新操作都寫入日誌,如果一個事務中的一部分操作成功,但以後的操作無法繼續,則通過回溯日誌,將已經操作成功的操作撤銷,從而達到全部操作失敗的目的。典型場景是,數據庫系統崩潰後重啓,此時數據庫處於不一致的狀態,必須先執行一個crash recovery的過程,讀取日誌進行redo(重演將所有已經執行但未成功寫入到磁盤的操作,保證持久性),再對所有到崩潰時尚未成功提交的事務進行undo(撤銷所有執行了一部分但尚未提交的操作,保證原子性)。crash recovery結束後,數據庫恢復到一致性狀態。
在多個事務並行進行的情況下,即時保證了每一個事務的原子性,仍然可能導致數據的不一致的結果。對此引入了隔離性,即保證每一個事務能夠看到的數據總是一致的,好像其他併發事務並不存在一樣。實現隔離性有兩種典型鎖:
一種是悲觀鎖,即當前事務將所有涉及操w作的對象加鎖,操作完成後釋放給其他對象使用。爲了儘可能提高性能。發明了各種粒度,各種性質的鎖,爲了解決死鎖問題,又發明了兩階段鎖協議等一些列技術。
樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設認爲數據一般情況下不會造成衝突,所以在數據進行提交更新的時候,纔會正式對數據的衝突與否進行檢測,如果發現衝突了,則讓返回用戶錯誤的信息,讓用戶決定如何去做。相對於悲觀鎖,在對數據庫進行處理的時候,樂觀鎖並不會使用數據庫提供的鎖機制。一般的實現樂觀鎖的方式就是記錄數據版本。數據版本,爲數據增加的一個版本標識。當讀取數據時,將版本標識的值一同讀出,數據每更新一次,同時對版本標識進行更新。當我們提交更新的時候,判斷數據庫表對應記錄的當前版本信息與第一次取出來的版本標識進行比對,如果數據庫表當前版本號與第一次取出來的版本標識值相等,則予以更新,否則認爲是過期數據。實現數據版本有兩種方式,第一種是使用版本號,第二種是使用時間戳。

4. Redis與關係型數據庫的同步問題

Redis是一個高性能的key-value數據庫。 redis的出現,很大程度補償了memcached這類key-value存儲的不足,在部 分場合可以對關係數據庫起到很好的補充作用。它提供了Python,Ruby,Erlang,PHP客戶端,使用很方便。
按照我們一般的使用Redis的場景應該是這樣的:

也就是說:我們會先去redis中判斷數據是否存在,如果存在,則直接返回緩存好的數據。而如果不存在的話,就會去數據庫中,讀取數據,並把數據緩存到Redis中。適用場合:如果數據量比較大,但不是經常更新的情況(比如用戶排行)

而第二種Redis的使用,跟第一種的情況完成不同,具體的情況請看:


這裏我們會先去Redis中判斷數據是否存在,如果存在,則直接更新對應的數據(吧對應更新過的key記錄下來,比如也保存到redis中,key爲save_update_keys),並把更新後的數據返回給頁面,而如果不保存的話,就會去先更新數據庫中的內容,然後把數據保存一份到Redis。後面的工作,後臺會有相關機制把Redis中的save_update_keys存儲的key,分別讀取出來,找到對應的數據,更新到DB中。優點:主要目的是把Redis當做數據庫使用,更新獲取數據比DB塊,非常適合大數據量的頻繁變動(比如微博),缺點,對Redis的依賴很大,要做好宕機時的數據保存。

5. 數據庫索引

數據庫索引,是數據庫管理系統中一個排序的數據結構,以協助快速查詢,更新數據庫表中數據,索引的實現通常使用B樹(所有節點的平衡因子均爲0的多叉查找樹)及其變種B+樹。

在數據之外,數據庫系統還維護着滿足特定查找算法的數據結構,這些數據結構以某種方式引用(指向)數據,這樣就可以在這些數據結構上實現高級查找算法。這種數據結構,就是索引。一是增加了數據庫的存儲空間二是在插入和修改數據時要花費較多的時間(因爲索引也要隨之變動)


上圖展示了一種可能的索引方式。左邊是數據表,一共有兩列七條記錄,最左邊的是數據記錄的物理地址(注意邏輯上相鄰的記錄在磁盤上也並不是一定物理相鄰的)。爲了加快Col2的查找,可以維護一個右邊所示的二叉查找樹,每個節點分別包含索引鍵值和一個指向對應數據記錄物理地址的指針,這樣就可以運用二叉查找在O(log2n)的複雜度內獲取到相應數據。


創建索引可以大大提高系統的性能。

第一,通過創建唯一性索引,可以保證數據庫表中每一行數據的唯一性。

第二,可以大大加快數據的檢索速度,這也是創建索引的最主要的原因。

第三,可以加速表和表之間的連接,特別是在實現數據的參考完整性方面特別有意義。

第四,在使用分組和排序子句進行數據檢索時,同樣可以顯著減少查詢中分組和排序的時間。

第五,通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的性能。 


也許會有人要問:增加索引有如此多的優點,爲什麼不對錶中的每一個列創建一個索引呢?因爲,增加索引也有許多不利的方面。

第一,創建索引和維護索引要耗費時間,這種時間隨着數據量的增加而增加。

第二,索引需要佔物理空間,除了數據表佔數據空間之外,每一個索引還要佔一定的物理空間,如果要建立聚簇索引,那麼需要的空間就會更大。

第三,當對錶中的數據進行增加、刪除和修改的時候,索引也要動態的維護,這樣就降低了數據的維護速度。


一般說來,應該在這些列上建立索引:

在經常需要搜索的列上,可以加快搜索的速度,

在作爲主鍵的列上,強制該列的唯一性和組織表中的排列結構

在經常用在連接的列上,這些列主要是一些外鍵,可以加快連接的速度

在需要根據範圍進行搜索的列上創建索引,因爲索引已經排序,其指定的範圍是連續的

在經常需要排序的列上創建索引,因爲索引已經排序,這樣查詢可以利用索引的排序,加快排序查詢的時間

在經常使用where子句中的列上面創建索引,加快條件的判斷速度。



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