1. 程式人生 > >螞蟻金服面試題及答案-總結

螞蟻金服面試題及答案-總結

前言

相信有人在網上看到過一樣的題,這裡我也是從某篇公眾號把題抄下來,答案都是筆者自己在網上搜的,適合即時回答,所以很多知識沒有引入太深。

一面

1、自我介紹、自己做的專案和技術領域

開放題

2、專案中的監控:那個監控指標常見的有哪些?

答:CPU、記憶體、IO 等等。建議下載個nmon工具,裡面有各個指標。

資料庫:Mysql(快取命中、索引、單條SQL效能、資料庫執行緒數、資料池連線數)

中介軟體:1.訊息2、負載均衡3、快取(包括執行緒數、連線數、日誌)。

網路: 吞吐量、吞吐率

應用: jvm記憶體、日誌、Full GC頻率

3、微服務涉及到的技術以及需要注意的問題有哪些?

4、註冊中心你瞭解了哪些?

答:Consul 、Eureka、ZooKeeper

5、consul 的可靠性你瞭解嗎?

6、consul 的機制你有沒有具體深入過?有沒有和其他的註冊中心對比過?

7、專案用 Spring 比較多,有沒有了解 Spring 的原理?AOP 和 IOC 的原理

答:(1). IoC(Inversion of Control)是指容器控制程式物件之間的關係,而不是傳統實現中,由程式程式碼直接操控。控制權由應用程式碼中轉到了外部容器,控制權的轉移是所謂反轉。 對於Spring而言,就是由Spring來控制物件的生命週期和物件之間的關係;IoC還有另外一個名字——“依賴注入(Dependency Injection)”。從名字上理解,所謂依賴注入,即元件之間的依賴關係由容器在執行期決定,即由容器動態地將某種依賴關係注入到元件之中。  

(2). 在Spring的工作方式中,所有的類都會在spring容器中登記,告訴spring這是個什麼東西,你需要什麼東西,然後spring會在系統執行到適當的時候,把你要的東西主動給你,同時也把你交給其他需要你的東西。所有的類的建立、銷燬都由 spring來控制,也就是說控制物件生存週期的不再是引用它的物件,而是spring。對於某個具體的物件而言,以前是它控制其他物件,現在是所有物件都被spring控制,所以這叫控制反轉。

(3). 在系統執行中,動態的向某個物件提供它所需要的其他物件。  

(4). 依賴注入的思想是通過反射機制實現的,在例項化一個類時,它通過反射呼叫類中set方法將事先儲存在HashMap中的類屬性注入到類中。 總而言之,在傳統的物件建立方式中,通常由呼叫者來建立被呼叫者的例項,而在Spring中建立被呼叫者的工作由Spring來完成,然後注入呼叫者,即所謂的依賴注入or控制反轉。 注入方式有兩種:依賴注入和設定注入; IoC的優點:降低了元件之間的耦合,降低了業務物件之間替換的複雜性,使之能夠靈活的管理物件。

AOP(Aspect Oriented Programming)

(1). AOP面向方面程式設計基於IoC,是對OOP的有益補充;

(2). AOP利用一種稱為“橫切”的技術,剖解開封裝的物件內部,並將那些影響了 多個類的公共行為封裝到一個可重用模組,並將其名為“Aspect”,即方面。所謂“方面”,簡單地說,就是將那些與業務無關,卻為業務模組所共同呼叫的 邏輯或責任封裝起來,比如日誌記錄,便於減少系統的重複程式碼,降低模組間的耦合度,並有利於未來的可操作性和可維護性。

(3). AOP代表的是一個橫向的關 系,將“物件”比作一個空心的圓柱體,其中封裝的是物件的屬性和行為;則面向方面程式設計的方法,就是將這個圓柱體以切面形式剖開,選擇性的提供業務邏輯。而 剖開的切面,也就是所謂的“方面”了。然後它又以巧奪天功的妙手將這些剖開的切面復原,不留痕跡,但完成了效果。

(4). 實現AOP的技術,主要分為兩大類:一是採用動態代理技術,利用擷取訊息的方式,對該訊息進行裝飾,以取代原有物件行為的執行;二是採用靜態織入的方式,引入特定的語法建立“方面”,從而使得編譯器可以在編譯期間織入有關“方面”的程式碼。

(5). Spring實現AOP:JDK動態代理和CGLIB代理 JDK動態代理:其代理物件必須是某個介面的實現,它是通過在執行期間建立一個介面的實現類來完成對目標物件的代理;其核心的兩個類是InvocationHandler和Proxy。 CGLIB代理:實現原理類似於JDK動態代理,只是它在執行期間生成的代理物件是針對目標類擴充套件的子類。CGLIB是高效的程式碼生成包,底層是依靠ASM(開源的java位元組碼編輯類庫)操作位元組碼實現的,效能比JDK強;需要引入包asm.jar和cglib.jar。     使用AspectJ注入式切面和@AspectJ註解驅動的切面實際上底層也是通過動態代理實現的。

(6). AOP使用場景:                     

Authentication 許可權檢查        

Caching 快取        

Context passing 內容傳遞        

Error handling 錯誤處理        

Lazy loading 延遲載入        

Debugging  除錯      

logging, tracing, profiling and monitoring 日誌記錄,跟蹤,優化,校準        

Performance optimization 效能優化,效率檢查        

Persistence  持久化        

Resource pooling 資源池        

Synchronization 同步        

Transactions 事務管理    

另外Filter的實現和struts2的攔截器的實現都是AOP思想的體現。

8、Spring Boot除了自動配置,相比傳統的 Spring 有什麼其他的區別?

為Spring 生態系統的開發提供一種更簡潔的方式,提供了很多非功能性特性,例如:嵌入式 Server,Security,統計,健康檢查,外部配置等等,主要體現在以下幾點:

1.Spring Boot可以建立獨立的Spring應用程式;

2.內嵌瞭如Tomcat,Jetty和Undertow這樣的容器,也就是說可以直接跑起來,用不著再做部署工作了;

3.無需再像Spring那樣搞一堆繁瑣的xml檔案的配置;

4.可以自動配置Spring。SpringBoot將原有的XML配置改為Java配置,將bean注入改為使用註解注入的方式(@Autowire),並將多個xml、properties配置濃縮在一個appliaction.yml配置檔案中。

5.提供了一些現有的功能,如量度工具,表單資料驗證以及一些外部配置這樣的一些第三方功能;

6.整合常用依賴(開發庫,例如spring-webmvc、jackson-json、validation-api和tomcat等),提供的POM可以簡化Maven的配置。當我們引入核心依賴時,SpringBoot會自引入其他依賴。

9、Spring Cloud 有了解多少?

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的開發便利性巧妙地簡化了分散式系統基礎設施的開發,如服務發現註冊、配置中心、訊息匯流排、負載均衡、斷路器、資料監控等,都可以用Spring Boot的開發風格做到一鍵啟動和部署。Spring Cloud並沒有重複製造輪子,它只是將目前各家公司開發的比較成熟、經得起實際考驗的服務框架組合起來,通過Spring Boot風格進行再封裝遮蔽掉了複雜的配置和實現原理,最終給開發者留出了一套簡單易懂、易部署和易維護的分散式系統開發工具包。

10、Spring Bean 的生命週期

一個Bean從建立到銷燬,如果是用BeanFactory來生成,管理Bean的話

Spring上下文中的Bean也類似,如下

    1、例項化一個Bean--也就是我們常說的new;

    2、按照Spring上下文對例項化的Bean進行配置--也就是IOC注入;

    3、如果這個Bean已經實現了BeanNameAware介面,會呼叫它實現的setBeanName(String)方法,此處傳遞的就是Spring配置檔案中Bean的id值

    4、如果這個Bean已經實現了BeanFactoryAware介面,會呼叫它實現的setBeanFactory(setBeanFactory(BeanFactory)傳遞的是Spring工廠自身(可以用這個方式來獲取其它Bean,只需在Spring配置檔案中配置一個普通的Bean就可以);

    5、如果這個Bean已經實現了ApplicationContextAware介面,會呼叫setApplicationContext(ApplicationContext)方法,傳入Spring上下文(同樣這個方式也可以實現步驟4的內容,但比4更好,因為ApplicationContext是BeanFactory的子介面,有更多的實現方法);

    6、如果這個Bean關聯了BeanPostProcessor介面,將會呼叫postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor經常被用作是Bean內容的更改,並且由於這個是在Bean初始化結束時呼叫那個的方法,也可以被應用於記憶體或快取技術;

    7、如果Bean在Spring配置檔案中配置了init-method屬性會自動呼叫其配置的初始化方法。

    8、如果這個Bean關聯了BeanPostProcessor介面,將會呼叫postProcessAfterInitialization(Object obj, String s)方法、;

    注:以上工作完成以後就可以應用這個Bean了,那這個Bean是一個Singleton的,所以一般情況下我們呼叫同一個id的Bean會是在內容地址相同的例項,當然在Spring配置檔案中也可以配置非Singleton,這裡我們不做贅述。

    9、當Bean不再需要時,會經過清理階段,如果Bean實現了DisposableBean這個介面,會呼叫那個其實現的destroy()方法;

    10、最後,如果這個Bean的Spring配置中配置了destroy-method屬性,會自動呼叫其配置的銷燬方法。

另外我們這裡描述的是應用Spring上下文Bean的生命週期,如果應用Spring的工廠也就是BeanFactory的話去掉5Ok

11、HashMap 和 hashTable 區別?

區別:Hashtable程安全的,效率比

Hashtable既不支援Null key也不支援Null valueHashtableput()方法的注中有

Hashtable的初始大小11,之後每次充,容量變為原來的2n+1

HashMap的初始化大小16。之後每次充,容量變為原來的2

Hashtable算元素的位置需要行一次除法運算,而除法運算是比

HashMap為了提高計算效率,將雜湊表的大小固定為了2冪,這樣在取模預算時,不需要做除法,只需要做位運算。位運算比除法的效率要高很多

HashMap承自AbstractMap類,而HashTable承自Dictionary類。不過它們都實現了同時實現了mapCloneable(可複製)、Serializable(可序列化)三個介面

12、Object 的 hashcode 方法重寫了,equals 方法要不要改?

不需要,Ojbect類中有兩個方法equals、hashCode,這兩個方法都是用來比較兩個物件是否相等的,如果兩個物件相等(equal),那麼必須擁有相同 的雜湊碼(hash code)

即使兩個物件有相同的雜湊值(hash code),他們不一定相等

重寫equals()方法就必須重寫hashCode(),但重寫hashcode方法不一定要重寫equals方法

13、Hashmap 執行緒不安全的出現場景

用ConcurrentHashMap 執行緒安全

多執行緒處理時hashmap執行緒不安全

首先hashmap裡size沒有用volatile字修,代表不是一個記憶體可量,執行緒操作資料的時候一般是從主存拷貝一個變數副本進行操作,操作完成後在把size寫回到主存size

執行緒不安全問題應該屬於併發問題之一的,屬於相對高階的問題了。這個時候的問題已經不僅僅侷限於程式碼層面了,很多時候需要結合JVM一起分析了

14、線上服務 CPU 很高該怎麼做?有哪些措施可以找到問題

定位出現問題的堆疊資訊排查具體問題

1、top命令:Linux命令。可以檢視實時的CPU使用情況。也可以檢視最近一段時間的CPU使用情況。

2、ps命令: Linux命令。強大的程序狀態監控命令。可以檢視程序以及程序中執行緒的當前CPU使用情況。屬於當前狀態的取樣資料。

3、jstack:  Java提供的命令。可以檢視某個程序的當前執行緒棧執行情況。根據這個命令的輸出可以定位某個程序的所有執行緒的當前執行狀態、執行程式碼,以及是否死鎖等等。

4、pstack:Linux命令。可以檢視某個程序的當前執行緒棧執行情況

 15、JDK 中有哪幾個執行緒池?順帶把執行緒池講了個遍

JUC提供了排程器物件Executors來建立執行緒池,可建立的執行緒池有四種

1、newFixedThreadPool建立一個指定工作執行緒數量的執行緒池。每當提交一個任務就建立一個工作執行緒,如果工作執行緒數量達到執行緒池初始的最大數,則將提交的任務存入到池佇列中。

2、newCachedThreadPool建立一個可快取的執行緒池。這種型別的執行緒池特點是:

1).工作執行緒的建立數量幾乎沒有限制(其實也有限制的,數目為Interger. MAX_VALUE), 這樣可靈活的往執行緒池中新增執行緒。

2).如果長時間沒有往執行緒池中提交任務,即如果工作執行緒空閒了指定的時間(預設為1分鐘),則該工作執行緒將自動終止。終止後,如果你又提交了新的任務,則執行緒池重新建立一個工作執行緒。

3、newSingleThreadExecutor建立一個單執行緒化的Executor,即只建立唯一的工作者執行緒來執行任務,如果這個執行緒異常結束,會有另一個取代它,保證順序執行(我覺得這點是它的特色)。單工作執行緒最大的特點是可保證順序地執行各個任務,並且在任意給定的時間不會有多個執行緒是活動的 。

4、newScheduleThreadPool建立一個定長的執行緒池,而且支援定時的以及週期性的任務執行,類似於Timer。(這種執行緒池原理暫還沒完全瞭解透徹)

16、SQL 優化的常見方法有哪些

  1. 查詢條件減少使用函式,避免全表掃描
  2. 減少不必要的表連線
  3. 有些資料操作的業務邏輯可以放到應用層進行實現
  4. 可以使用with as
  5. 儘量避免使用遊標,因為遊標的效率較差
  6. 不要把SQL語句寫得太複雜
  7. 不能迴圈執行查詢
  8. 用 exists 代替 in 
  9. 表關聯關係不要太糾結
  10. 查詢多用索引列取查,用charindex或者like[0-9]來代替%%
  11. inner關聯的表可以先查出來,再去關聯leftjoin的表
  12. 可以進行表關聯資料拆分,即先查出核心資料,再通過核心資料查其他資料,這樣會快得多
  13. 參考SQL執行順序進行優化
  14. 表關聯時取別名,也能提高效率
  15. 使用檢視,給檢視建立索引進行優化
  16. 使用資料倉庫的形式,建立單獨的表儲存資料,根據時間戳定期更新資料。將多表關聯的資料集中抽取存入一張表中,查詢時單表查詢,提高了查詢效率
  17. 對查詢進行優化,應儘量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引
  18. 應儘量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:    
select id from t where num is null     
可以在num上設定預設值0,確保表中num列沒有null值,然後這樣查詢:   
select id from t where num=0       

   19.應儘量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描

17、SQL 索引的順序,欄位的順序

18、檢視 SQL 是不是使用了索引?(有什麼工具)

在select語句前加上EXPLAIN即可

19、TCP 和 UDP 的區別?TCP 資料傳輸過程中怎麼做到可靠的?

UDP(User Data Protocol,使用者資料報協議)是與TCP相對應的協議。它是屬於TCP/IP協議族中的一種

1)為了保證資料包的可靠傳遞,傳送方必須把已傳送的資料包保留在緩衝區; 
(2)併為每個已傳送的資料包啟動一個超時定時器; 
(3)如在定時器超時之前收到了對方發來的應答資訊(可能是對本包的應答,也可以是對本包後續包的應答),則釋放該資料包占用的緩衝區; 
(4)否則,重傳該資料包,直到收到應答或重傳次數超過規定的最大次數為止。
(5)接收方收到資料包後,先進行CRC校驗,如果正確則把資料交給上層協議,然後給傳送方傳送一個累計應答包,表明該資料已收到,如果接收方正好也有資料要發給傳送方,應答包也可方在資料包中捎帶過去。

20、說下你知道的排序演算法吧

常見的內部排序演算法有:插入排序、希爾排序、選擇排序、氣泡排序、歸併排序、快速排序、堆排序、基數排序等

21、查詢一個數組的中位數?

通過二分查詢法來找中位數

基本思想是:假設ar1[i]是合併後的中位數,那麼ar1[i]大於ar1[]中前i-1個數,且大於ar2[]中前j=n-i-1個數。通過ar1[i]和ar2[j]、ar2[j+1]兩個數的比較,在ar1[i]的左邊或者ar1[i]右邊繼續進行二分查詢。對於兩個陣列 ar1[] 和ar2[], 先在 ar1[] 中做二分查詢。如果在ar1[]中沒找到中位數, 繼續在ar2[]中查詢。

演算法流程:
1) 得到陣列ar1[]最中間的數,假設下標為i.
2) 計算對應在陣列ar2[]的下標j,j = n-i-1 
3) 如果 ar1[i] >= ar2[j] and ar1[i] <= ar2[j+1],那麼 ar1[i] 和 ar2[j] 就是兩個中間元素,返回ar2[j] 和 ar1[i] 的平均值
4) 如果 ar1[i] 大於 ar2[j] 和 ar2[j+1] 那麼在ar1[i]的左部分做二分查詢(i.e., arr[left ... i-1])
5) 如果 ar1[i] 小於 ar2[j] 和 ar2[j+1] 那麼在ar1[i]的右部分做二分查詢(i.e., arr[i+1....right])
6) 如果到達陣列ar1[]的邊界(left or right),則在陣列ar2[]中做二分查詢

時間複雜度:O(logn)。

二面

22、你有什麼問題想問我的嗎?
1、自我介紹、工作經歷、技術棧

2、專案中你學到了什麼技術?

3、微服務劃分的粒度?

4、微服務的高可用怎麼保證的

負載均衡與反向代理,隔離,限流,降級,超時與重試,回滾,壓力測試與應急預案

5、常用的負載均衡,該怎麼用,你能說下嗎?

1、http重定向

當http代理(比如瀏覽器)向web伺服器請求某個URL後,web伺服器可以通過http響應頭資訊中的Location標記來返回一個新的URL。這意味著HTTP代理需要繼續請求這個新的URL,完成自動跳轉。

2、DNS負載均衡

DNS 負責提供域名解析服務,當訪問某個站點時,實際上首先需要通過該站點域名的DNS伺服器來獲取域名指向的IP地址,在這一過程中,DNS伺服器完成了域名到IP地址的對映,同樣,這樣對映也可以是一對多的,這時候,DNS伺服器便充當了負載均衡排程器,它就像http重定向轉換策略一樣,將使用者的請求分散到多臺伺服器上,但是它的實現機制完全不同。

3、反向代理負載均衡

這個肯定大家都有所接觸,因為幾乎所有主流的Web伺服器都熱衷於支援基於反向代理的負載均衡。它的核心工作就是轉發HTTP請求。

相比前面的HTTP重定向和DNS解析,反向代理的排程器扮演的是使用者和實際伺服器中間人的角色:

     1、任何對於實際伺服器的HTTP請求都必須經過排程器

     2、排程器必須等待實際伺服器的HTTP響應,並將它反饋給使用者(前兩種方式不需要經過排程反饋,是實際伺服器直接傳送給使用者)

4、IP負載均衡(LVS-NAT)

因為反向代理伺服器工作在HTTP層,其本身的開銷就已經嚴重製約了可擴充套件性,從而也限制了它的效能極限。那能否在HTTP層面以下實現負載均衡呢?

NAT伺服器:它工作在傳輸層,它可以修改傳送來的IP資料包,將資料包的目標地址修改為實際伺服器地址

5、直接路由(LVS-DR)

NAT是工作在網路分層模型的傳輸層(第四層),而直接路由是工作在資料鏈路層(第二層),貌似更屌些。它通過修改資料包的目標MAC地址(沒有修改目標IP),將資料包轉發到實際伺服器上,不同的是,實際伺服器的響應資料包將直接傳送給客戶羰,而不經過排程器

6、IP隧道(LVS-TUN)

基於IP隧道的請求轉發機制:將排程器收到的IP資料包封裝在一個新的IP資料包中,轉交給實際伺服器,然後實際伺服器的響應資料包可以直接到達使用者端。目前Linux大多支援,可以用LVS來實現,稱為LVS-TUN,與LVS-DR不同的是,實際伺服器可以和排程器不在同一個WANt網段,排程器通過 IP隧道技術來轉發請求到實際伺服器,所以實際伺服器也必須擁有合法的IP地址。

總體來說,LVS-DR和LVS-TUN都適合響應和請求不對稱的Web伺服器,如何從它們中做出選擇,取決於你的網路部署需要,因為LVS-TUN可以將實際伺服器根據需要部署在不同的地域,並且根據就近訪問的原則來轉移請求,所以有類似這種需求的,就應該選擇LVS-TUN。

6、閘道器能夠為後端服務帶來哪些好處?

後端伺服器可以專心處理業務請求,節省了大量連線管理的開銷

7、Spring Bean 的生命週期

8、xml 中配置的 init、destroy 方法怎麼可以做到呼叫具體的方法?

9、反射的機制

大家都知道,要讓Java程式能夠執行,那麼就得讓Java類要被Java虛擬機器載入。Java類如果不被Java虛擬機器載入,是不能正常執行的。現在我們執行的所有的程式都是在編譯期的時候就已經知道了你所需要的那個類的已經被載入了。

Java的反射機制是在編譯並不確定是哪個類被載入了,而是在程式執行的時候才載入、探知、自審。使用在編譯期並不知道的類。這樣的特點就是反射

反射機制通過void setAccessible(boolean flag)方法可以得到一個類的private的方法和屬性,使用這些private的方法和屬性

10、Object 類中的方法

1,建構函式 
2hashCodeequale函式用來判斷象是否相同
3wait(),wait(long),wait(long,int),notify(),notifyAll() 
4toString()getClass, 
5clone() 
6finalize()用於在垃圾回收

11、hashcode 和 equals 方法常用地方

12、物件比較是否相同

equals通常用來比較兩個物件的內容是否相等,==用來比較兩個物件的地址是否相等

13、hashmap put 方法存放的時候怎麼判斷是否是重複的

先比keyhashCode,再比相等或equals的,所以重寫hashCode()equals()方法即可實現新增重複元素。

14、Object toString 方法常用的地方,為什麼要重寫該方法

常用在物件模型類

因為假如User是一個使用者的物件,如果User.toString();結果是不正常的,因為User物件中可能有多個屬性,如年齡,姓名等,這個toString後無法知道具體的是那個屬性轉換為字串

15、Set 和 List 區別?

Set(集):集合中的物件不按特定方式排序,並且沒有重複物件。它的有些實現類能對集合中的物件按特定方式排序。
List(列表):集合中的物件按索引位置排序,可以有重複物件,允許按照物件在集合中的索引位置檢索物件。

16、ArrayList 和 LinkedList 區別

 ArrayList是實現了基於動態陣列的資料結構,LinkedList基於連結串列的資料結構

ArrayList 繼承AbstractList
LinkedList 繼承AbstractSequentialList

ArrayList 採用的是陣列形式來儲存物件的,這種方式將物件放在連續的位置中,所以最大的缺點就是插入刪除時非常麻煩

LinkedList 採用的將物件存放在獨立的空間中,而且在每個空間中還儲存下一個連結的索引 但是缺點就是查詢非常麻煩 要叢第一個索引開始

17、如果存取相同的資料,ArrayList 和 LinkedList 誰佔用空間更大?

對於隨機訪問get和set,ArrayList覺得優於LinkedList,因為LinkedList要移動指標

對於新增和刪除操作add和remove,LinedList比較佔優勢,因為ArrayList要移動資料,若要從陣列中刪除或插入某一個物件,需要移動後段的陣列元素,從而會重新調整索引順序,調整索引順序會消耗一定的時間,相反,LinkedList是使用連結串列實現的,若要從連結串列中刪除或插入某一個物件,只需要改變前後物件的引用即可

18、Set 存的順序是有序的嗎?

無序

Set是Map的一個馬甲,主要邏輯都交給Map實現

19、常見 Set 的實現有哪些?

HashSet

LinkedHashSet

TreeSet

20、TreeSet 對存入對資料有什麼要求呢?

TreeSet集合是用來物件元素進行排序的,同樣他也可以保證元素的唯一

21、HashSet 的底層實現呢?

22、TreeSet 底層原始碼有看過嗎?

TreeSet的底層實現是TreeMap

public TreeSet(Comparator<? super E> comparator) {

        this(new TreeMap<>(comparator));

    }

23、HashSet 是不是執行緒安全的?為什麼不是執行緒安全的?

說白了,HashSet就是限制了功能的HashMap,所以瞭解HashMap的實現原理

24、Java 中有哪些執行緒安全的 Map?

Concurrenthashmap

25、Concurrenthashmap 是怎麼做到執行緒安全的?

ConcurrentHashMap的大部分操作和HashMap是相同的,例如初始化,擴容和連結串列向紅黑樹的轉變等。但是,在ConcurrentHashMap中,大量使用了U.compareAndSwapXXX

的方法,這個方法是利用一個CAS演算法實現無鎖化的修改值的操作,他可以大大降低鎖代理的效能消耗。這個演算法的基本思想就是不斷地去比較當前記憶體中的變數值與你指定的

一個變數值是否相等,如果相等,則接受你指定的修改的值,否則拒絕你的操作。因為當前執行緒中的值已經不是最新的值,你的修改很可能會覆蓋掉其他執行緒修改的結果。這一

點與樂觀鎖,SVN的思想是比較類似的。

同時,在ConcurrentHashMap中還定義了三個原子操作,用於對指定位置的節點進行操作。這三種原子操作被廣泛的使用在ConcurrentHashMap的get和put等方法中,

正是這些原子操作保證了ConcurrentHashMap的執行緒安全。

在ConcurrentHashMap沒有出現以前,jdk使用hashtable來實現執行緒安全,但是hashtable是將整個hash表鎖住,所以效率很低下。

ConcurrentHashMap將資料分別放到多個Segment中,預設16個,每一個Segment中又包含了多個HashEntry列表陣列,

對於一個key,需要經過三次hash操作,才能最終定位這個元素的位置,這三次hash分別為:

  1. 對於一個key,先進行一次hash操作,得到hash值h1,也即h1 = hash1(key);
  2. 將得到的h1的高几位進行第二次hash,得到hash值h2,也即h2 = hash2(h1高几位),通過h2能夠確定該元素的放在哪個Segment;
  3. 將得到的h1進行第三次hash,得到hash值h3,也即h3 = hash3(h1),通過h3能夠確定該元素放置在哪個HashEntry。

每一個Segment都擁有一個鎖,當進行寫操作時,只需要鎖定一個Segment,而其它Segment中的資料是可以訪問的。

26、HashTable 你瞭解過嗎?

Hashtable既不支援Null key也不支援Null value。Hashtable的put()方法的註釋中有說明

Hashtable是執行緒安全的,

Hashtable是執行緒安全的,它的每個方法中都加入了Synchronize方法,效率比較低

Hashtable預設的初始大小為11,之後每次擴充,容量變為原來的2n+1。

Hashtable在計算元素的位置時需要進行一次除法運算,而除法運算是比較耗時的。

27、如何保證執行緒安全問題?

28、synchronized、lock

synchronized是java中的一個關鍵字,也就是說是Java語言內建的特性

如果一個程式碼塊被synchronized修飾了,當一個執行緒獲取了對應的鎖,並執行該程式碼塊時,其他執行緒便只能一直等待,等待獲取鎖的執行緒釋放鎖,而這裡獲取鎖的執行緒釋放鎖只會有兩種情況:

  1)獲取鎖的執行緒執行完了該程式碼塊,然後執行緒釋放對鎖的佔有;

  2)執行緒執行發生異常,此時JVM會讓執行緒自動釋放鎖

那麼如果這個獲取鎖的執行緒由於要等待IO或者其他原因(比如呼叫sleep方法)被阻塞了,但是又沒有釋放鎖,其他執行緒便只能乾巴巴地等待,試想一下,這多麼影響程式執行效率。

  因此就需要有一種機制可以不讓等待的執行緒一直無期限地等待下去(比如只等待一定的時間或者能夠響應中斷),通過Lock就可以辦到

再舉個例子:當有多個執行緒讀寫檔案時,讀操作和寫操作會發生衝突現象,寫操作和寫操作會發生衝突現象,但是讀操作和讀操作不會發生衝突現象。

  但是採用synchronized關鍵字來實現同步的話,就會導致一個問題:

  如果多個執行緒都只是進行讀操作,所以當一個執行緒在進行讀操作時,其他執行緒只能等待無法進行讀操作。

  因此就需要一種機制來使得多個執行緒都只是進行讀操作時,執行緒之間不會發生衝突,通過Lock就可以辦到。

另外,通過Lock可以知道執行緒有沒有成功獲取到鎖。這個是synchronized無法辦到的

29、volatile 的原子性問題?為什麼 i++ 這種不支援原子性?從計算機原理的設計來講下不能保證原子性的原因

30、happens before 原理

31、cas 操作

java.util.concurrent包中藉助CAS實現了區別於synchronized同步鎖的一種樂觀鎖

cas是比較並交換演算法

 CAS有3個運算元,記憶體值V,舊的預期值A,要修改的新值B。當且僅當預期值A和記憶體值V相同時,將記憶體值V修改為B,否則什麼都不做

JDK提供了AtomicReference類來保證引用物件之間的原子性,就可以把多個變數放在一個物件裡來進行CAS操作。

32、lock 和 synchronized 的區別?

1)Lock是一個介面,而synchronized是Java中的關鍵字,synchronized是內建的語言實現;

  2)synchronized在發生異常時,會自動釋放執行緒佔有的鎖,因此不會導致死鎖現象發生;而Lock在發生異常時,如果沒有主動通過unLock()去釋放鎖,則很可能造成死鎖現象,因此使用Lock時需要在finally塊中釋放鎖;

  3)Lock可以讓等待鎖的執行緒響應中斷,而synchronized卻不行,使用synchronized時,等待的執行緒會一直等待下去,不能夠響應中斷;

  4)通過Lock可以知道有沒有成功獲取鎖,而synchronized卻無法辦到。

  5)Lock可以提高多個執行緒進行讀操作的效率。

  在效能上來說,如果競爭資源不激烈,兩者的效能是差不多的,而當競爭資源非常激烈時(即有大量執行緒同時競爭),此時Lock的效能要遠遠優於synchronized。所以說,在具體使用時要根據適當情況選擇。

類別

synchronized

Lock

存在

Java的關字,在jvm層面

是一個

鎖的釋

1、以行完同步代 2生異常,jvm讓線

finally中必須釋,不然容易造成程死

鎖的獲

A執行緒獲得鎖,B執行緒等待。如果A執行緒阻塞,B執行緒會一直等

分情況而定,Lock有多個鎖獲取的方式,具體下面會道,大致就是可以嘗試獲程可以不用一直等待

鎖狀態