Java面試題—中級(中)


什么是線程死鎖?死鎖如何產生?如何避免線程死鎖?

 

死鎖的介紹:

 

線程死鎖是指由於兩個或者多個線程互相持有對方所需要的資源,導致這些線程處於等待狀態,無法前往執行。當線程進入對象的synchronized代碼塊時,便占有了資源,直到它退出該代碼塊或者調用wait方法,才釋放資源,在此期間,其他線程將不能進入該代碼塊。當線程互相持有對方所需要的資源時,會互相等待對方釋放資源,如果線程都不主動釋放所占有的資源,將產生死鎖。

 

死鎖的產生的一些特定條件:

 

1、互斥條件:進程對於所分配到的資源具有排它性,即一個資源只能被一個進程占用,直到被該進程釋放 。

 

2、請求和保持條件:一個進程因請求被占用資源而發生阻塞時,對已獲得的資源保持不放。 

 

3、不剝奪條件:任何一個資源在沒被該進程釋放之前,任何其他進程都無法對他剝奪占用。

 

4、循環等待條件:當發生死鎖時,所等待的進程必定會形成一個環路(類似於死循環),造成永久阻塞。

 

如何避免:

 

1、加鎖順序:

當多個線程需要相同的一些鎖,但是按照不同的順序加鎖,死鎖就很容易發生。如果能確保所有的線程都是按照相同的順序獲得鎖,那么死鎖就不會發生。當然這種方式需要你事先知道所有可能會用到的鎖,然而總有些時候是無法預知的。

 

2、加鎖時限:

加上一個超時時間,若一個線程沒有在給定的時限內成功獲得所有需要的鎖,則會進行回退並釋放所有已經獲得的鎖,然后等待一段隨機的時間再重試。但是如果有非常多的線程同一時間去競爭同一批資源,就算有超時和回退機制,還是可能會導致這些線程重復地嘗試但卻始終得不到鎖。

 

3、死鎖檢測:

死鎖檢測即每當一個線程獲得了鎖,會在線程和鎖相關的數據結構中(map、graph等等)將其記下。除此之外,每當有線程請求鎖,也需要記錄在這個數據結構中。死鎖檢測是一個更好的死鎖預防機制,它主要是針對那些不可能實現按序加鎖並且鎖超時也不可行的場景。

 


 

notify和notifyAll區別

 

他們的作用都是通知處於等待該對象的線程。

 

1、notifyAll使所有原來在該對象上等待被notify的線程統統退出wait的狀態,變成等待該對象上的鎖,一旦該對象被解鎖,他們就會去競爭。

 

2、notify是通知其中一個線程,不會通知所有的線程。

 


 

談一談對MySQL InnoDB的認識

 

介紹:

 

InnoDB引擎是MySQL數據庫的一個重要的存儲引擎,和其他存儲引擎相比,InnoDB引擎的優點是支持兼容ACID的事務(類似於PostgreSQL),以及參數完整性(有外鍵)等。現在Innobase實行雙認證授權.MySQL5.5.5以后默認的存儲引擎都是InnoDB引擎。

 

特點是:

 

1、具有較好的事務支持:支持4個事務隔離級別,支持多版本讀

2、行級鎖定:通過索引實現,全表掃描仍然會是表鎖,注意間隙鎖的影響

3、讀寫阻塞與事務隔離級別相關

4、具有非常高效的緩存特性:能緩存索引,也能緩存數據

5、整個表和主鍵以Cluster方式存儲,組成一顆平衡樹

6、所有Secondary Index都會保存主鍵信息

 

適用場景:

 

1、需要事務支持(具有較好的事務特性)

2、行級鎖定對高並發有很好的適應能力,但需要確保查詢是通過索引完成

3、數據更新較為頻繁的場景

4、數據一致性要求較高

5、硬件設備內存較大,可以利用InnoDB較好的緩存能力來提高內存利用率,盡可能減少磁盤IO

 


 

談一談數據庫事務的隔離級別?

 

1、Read uncommitted(讀未提交)就是一個事務可以讀取另一個未提交事務的數據。

 

2、Read committed(讀提交)就是一個事務要等另一個事務提交后才能讀取數據。

 

3、Repeatable read(重復讀)就是在開始讀取數據(事務開啟)時,不再允許修改操作。

 

4、Serializable(序列化)在該級別下,事務串行化順序執行,可以避免臟讀、不可重復讀與幻讀。是最高的事務隔離級別,但是這種事務隔離級別效率低下,比較耗數據庫性能,一般不使用。

 

事務的作用就是保證數據的一致性、完整性。事務隔離級別越高,在並發下會產生的問題就越少,但同時付出的性能消耗也將越大,因此很多時候必須在並發性和性能之間做一個權衡。所以設立了幾種事務隔離級別,以便讓不同的項目可以根據自己項目的並發情況選擇合適的事務隔離級別,對於在事務隔離級別之外會產生的並發問題,在代碼中做補償。

 


 

MySQL主備同步的基本原理

 

MySQL支持單向、異步復制,復制過程中一個服務器充當主服務器,而一個或多個其它服務器充當從服務器。

 

MySQL復制是基於主服務器在二進制日志中跟蹤所有對數據庫的更改。因此,要進行復制,必須在主服務器上啟用二進制日志。每個從服務器從主服務器接收主服務器已經記錄到日志的數據。

 

當一個從服務器連接主服務器時,它通知主服務器從服務器在日志中讀取的最后一次成功更新的位置。從服務器接收從那時起發生的任何更新,並在本機上執行相同的更新。然后封鎖並等待主服務器通知新的更新。從服務器執行備份不會干擾主服務器,在備份過程中主服務器可以繼續處理更新。

 


 

 

Java語言中一個顯著的特點就是引入了垃圾回收機制,這個大家都清楚,垃圾回收的概念這里也不做介紹,重點是垃圾回收是在什么時候開始?對什么東西,做了什么事情?

 

GC何時開始:

所有的回收器類型都是基於分代技術來實現的,那就必須要清楚對象按其生命周期是如何划分的。

  • 年輕代:划分為三個區域:原始區(Eden)和兩個小的存活區(Survivor),兩個存活區按功能分為From和To。絕大多數的對象都在原始區分配,超過一個垃圾回收操作仍然存活的對象放到存活區。垃圾回收絕大部分發生在年輕代。

  • 年老代:存儲年輕代中經過多個回收周期仍然存活的對象,對於一些大的內存分配,也可能直接分配到永久代。

  • 持久代:存儲類、方法以及它們的描述信息,這里基本不產生垃圾回收。

 

有了以上這些鋪墊之后開始回答GC何時開始:

Eden內存滿了之后,開始Minor GC(從年輕代空間回收內存被稱為 Minor GC);升到老年代的對象所需空間大於老年代剩余空間時開始Full GC(但也可能小於剩余空間時,被HandlePromotionFailure參數強制Full GC)

 

對什么東西操作,即垃圾回收的對象是什么:

從root開始搜索沒有可達對象,而且經過第一次標記、清理后,仍然沒有復活的對象。 

 

做了什么東西:

主要做了清理對象,整理內存的工作。具體的引申如下

 

垃圾回收器的類型:

  • 串行垃圾回收器(Serial Garbage Collector)

  • 並行垃圾回收器(Parallel Garbage Collector)

  • 並發標記掃描垃圾回收器(CMS Garbage Collector)

  • G1垃圾回收器(G1 Garbage Collector)

 

垃圾回收算法:

  • 引用計數法

  • 標記清除法

  • 復制算法

  • 標記壓縮算法

  • 分代算法

  • 分區算法

 


 

 

類在虛擬機中的加載過程

 

加載Loading:

通過一個類的全限定名來獲取一個二進制字節流、將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構、在內存中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種數據的訪問入口。

 

驗證Verification:

確保Class文件的字節流中包含的信息符合當前虛擬機的要求,並不會危害虛擬機的自身安全。

 

准備Preparation:

正式為類變量分配內存並設置類變量初始值。

 

解析Resolution:

虛擬機將常量池內的符號引用替換為直接引用的過程。

 

初始化Initialization:

類加載過程的最后一步,到了這個階段才真正開始執行類中定義的Java程序代碼。

 

使用Using:

根據你寫的程序代碼定義的行為執行。

 

卸載Unloading:

GC負責卸載,這部分一般不用討論。

 


 

 

強引用、軟引用、弱引用、虛引用與GC的關系

 

強引用:new出的對象之類的引用,只要強引用還在,永遠不會回收。

軟引用:引用但非必須的對象,內存溢出異常之前回收。 

弱引用:非必須的對象,對象只能生存到下一次垃圾收集發生之前。 

虛引用:對生存時間無影響,在垃圾回收時得到通知。

 


 

 

說一下spring中Bean的作用域

 

singleton:

    Spring IoC容器中只會存在一個共享的Bean實例,無論有多少個Bean引用它,始終指向同一對象。Singleton作用域是Spring中的缺省作用域。

prototype:

    每次通過Spring容器獲取prototype定義的bean時,容器都將創建一個新的Bean實例,每個Bean實例都有自己的屬性和狀態,而singleton全局只有一個對象。

request:

    在一次Http請求中,容器會返回該Bean的同一實例。而對不同的Http請求則會產生新的Bean,而且該bean僅在當前Http Request內有效。

session:

    在一次Http Session中,容器會返回該Bean的同一實例。而對不同的Session請求則會創建新的實例,該bean實例僅在當前Session內有效。

global Session:

    在一個全局的Http Session中,容器會返回該Bean的同一個實例,僅在使用portlet context時有效。

 

 


 

 

說一下spring中Bean的生命周期

 

  • 實例化一個Bean,也就是我們通常說的new。

  • 按照Spring上下文對實例化的Bean進行配置,也就是IOC注入。

  • 如果這個Bean實現了BeanNameAware接口,會調用它實現的setBeanName(String beanId)方法,此處傳遞的是Spring配置文件中Bean的ID。

  • 如果這個Bean實現了BeanFactoryAware接口,會調用它實現的setBeanFactory(),傳遞的是Spring工廠本身(可以用這個方法獲取到其他Bean)。

  • 如果這個Bean實現了ApplicationContextAware接口,會調用setApplicationContext(ApplicationContext)方法,傳入Spring上下文。

  • 如果這個Bean關聯了BeanPostProcessor接口,將會調用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor經常被用作是Bean內容的更改,並且由於這個是在Bean初始化結束時調用After方法,也可用於內存或緩存技術。

  • 如果這個Bean在Spring配置文件中配置了init-method屬性會自動調用其配置的初始化方法。

  • 如果這個Bean關聯了BeanPostProcessor接口,將會調用postAfterInitialization(Object obj, String s)方法。

  • 當Bean不再需要時,會經過清理階段,如果Bean實現了DisposableBean接口,會調用其實現的destroy方法。

  • 最后,如果這個Bean的Spring配置中配置了destroy-method屬性,會自動調用其配置的銷毀方法。

 


 

 

對Spring中依賴注入兩種方式的認識

 

兩種注入方式為:構造方法注入和設值注入

  1. 設值注入與傳統的JavaBean的寫法更相似,程序員更容易理解、接受,通過setter方式設定依賴關系顯得更加直觀、明顯;

  2. 對於復雜的依賴關系,如果采用構造注入,會導致構造器過於臃腫,難以閱讀。Spring在創建Bean實例時,需要同時實例化其依賴的全部實例,因而會產生浪費。而使用設置注入,則避免這下問題;

  3. 在某些屬性可選的情況下,多參數的構造器更加笨拙,官方更鼓勵使用設值注入。

  4. 構造注入可以在構造器中決定依賴關系的注入順序,優先依賴的優先注入。

  5. 對於依賴關系無須變化的Bean,構造注入更有用處,因為沒有setter方法,所有的依賴關系全部在構造器內設定,因此,不用擔心后續代碼對依賴關系的破壞。

  6. 構造注入使依賴關系只能在構造器中設定,則只有組件的創建者才能改變組件的依賴關系。對組件的調用者而言,組件內部的依賴關系完全透明,更符合高內聚的原則。

  7. 設值注入不會重寫構造方法的值。如果我們對同一個變量同時使用了構造方法注入又使用了設置方法注入的話,那么構造方法將不能覆蓋由設值方法注入的值。

  8. 建議采用以設值注入為主,構造注入為輔的注入策略。對於依賴關系無須變化的注入,盡量采用構造注入;而其他的依賴關系的注入,則考慮采用set注入。

 


 

 

Spring框架中都用到了哪些設計模式?

 

  • 代理模式:在AOP和remoting中被用的比較多。

  • 單例模式:在spring配置文件中定義的bean默認為單例模式。

  • 模板方法模式:用來解決代碼重復的問題。

  • 前端控制器模式:Spring提供了DispatcherServlet來對請求進行分發。

  • 依賴注入模式:貫穿於BeanFactory / ApplicationContext接口的核心理念。

  • 工廠模式:BeanFactory用來創建對象的實例。

 


 

 

BeanFactory 和ApplicationContext的區別

 

BeanFactory和ApplicationContext都是接口,並且ApplicationContext是BeanFactory的子接口。

 

BeanFactory是Spring中最底層的接口,提供了最簡單的容器的功能,只提供了實例化對象和拿對象的功能。而ApplicationContext是Spring的一個更高級的容器,提供了更多的有用的功能。 

 

ApplicationContext提供的額外的功能:國際化的功能、消息發送、響應機制、統一加載資源的功能、強大的事件機制、對Web應用的支持等等。

 

加載方式的區別:BeanFactory采用的是延遲加載的形式來注入Bean;ApplicationContext則相反的,它是在Ioc啟動時就一次性創建所有的Bean,好處是可以馬上發現Spring配置文件中的錯誤,壞處是造成浪費。

 


 

 

數據庫的三大范式

 

1 、第一范式(1NF)

 

在任何一個關系數據庫中,第一范式(1NF)是對關系模式的基本要求,不滿足第一范式(1NF)的數據庫就不是關系數據庫。 

 

所謂第一范式(1NF)是指數據庫表的每一列都是不可分割的基本數據項,同一列中不能有多個值,即實體中的某個屬性不能有多個值或者不能有重復的屬性。如果出現重復的屬性,就可能需要定義一個新的實體,新的實體由重復的屬性構成,新實體與原實體之間為一對多關系。

 

在第一范式(1NF)中表的每一行只包含一個實例的信息。簡而言之,第一范式要求數據表中的每一列(每個字段)必須是不可拆分的最小單元。

 

2、 第二范式(2NF)

 

第二范式(2NF)是在第一范式(1NF)的基礎上建立起來的,即滿足第二范式(2NF)必須先滿足第一范式(1NF)。第二范式(2NF)要求數據庫表中的每個實例或行必須可以被惟一地區分。為實現區分通常需要為表加上一個列,以存儲各個實例的惟一標識。

 

第二范式(2NF)要求實體的屬性完全依賴於主關鍵字。所謂完全依賴是指不能存在僅依賴主關鍵字一部分的屬性,如果存在,那么這個屬性和主關鍵字的這一部分應該分離出來形成一個新的實體,新實體與原實體之間是一對多的關系。為實現區分通常需要為表加上一個列,以存儲各個實例的惟一標識。簡而言之,第二范式要求表中的所有列,都必須依賴於主鍵,而不能有任何一列與主鍵沒有關系。

 

3 、第三范式(3NF) 

 

滿足第三范式(3NF)必須先滿足第二范式(2NF)。第三范式(3NF)要求一個數據庫表中不包含其它表中已包含的非主關鍵字信息。簡而言之,第三范式要求表中的每一列只與主鍵直接相關而不是間接相關,表中的每一列只能依賴於主鍵。

 


 

TCP和UDP的區別及其適用場景

 

首先說一下什么是TCP和UDP:

TCP是傳輸控制協議,提供的是面向連接、可靠的字節流服務。

UDP是用戶數據報協議,是一個簡單的面向數據報的運輸層協議。

 

TCP和UDP的區別:

  • TCP面向連接的運輸層協議,UDP無連接

  • TCP是可靠交付,UDP是盡最大努力交付

  • TCP面向字節流,UDP面向報文

  • TCP是點對點連接的,UDP一對一,一對多,多對多都可以

  • TCP適合用於網頁,郵件等,UDP適合用於視頻,語音廣播等

 

TCP和UDP的適用場景:

整個數據要准確無誤的傳遞給對方,這往往用於一些要求可靠的應用,比如HTTP、HTTPS、FTP等傳輸文件的協議,POP、SMTP等郵件傳輸的協議。 

 

當對網絡通訊質量要求不高的時候,要求網絡通訊速度能盡量的快,比如視頻、廣播等,這時就可以使用UDP。 

 


 

說一下Spring的核心模塊

 

  • Spring Core【核心容器】:核心容器提供了Spring的基本功能。核心容器的核心功能是用IOC容器來管理類的依賴關系。

     

  • Spring AOP【面向切面】:Spring的AOP模塊提供了面向切面編程的支持。SpringAOP采用的是純Java實現,采用基於代理的AOP實現方案,AOP代理由IOC容器負責生成、管理,依賴關系也一並由IOC容器管理。

     

  • Spring ORM【對象實體映射】:提供了與多個第三方持久層框架的良好整合。

 

  • Spring DAO【持久層模塊】: Spring進一步簡化DAO開發步驟,能以一致的方式使用數據庫訪問技術,用統一的方式調用事務管理,避免具體的實現侵入業務邏輯層的代碼中。

 

  • Spring Context【應用上下文】:它是一個配置文件,為Spring提供上下文信息,提供了框架式的對象訪問方法。

 

  • Spring Web【Web模塊】:提供了基礎的針對Web開發的集成特性。

 

  • Spring MVC【MVC模塊】:提供了Web應用的MVC實現。Spring的MVC框架並不是僅僅提供一種傳統的實現,它提供了一種清晰的分離模型。

 


 

(轉發)forward與(重定向)redirect的區別

 

  • forward是服務器請求資源,服務器直接訪問目標地址的URL,把那個URL的響應內容讀取過來,然后把這些內容再發給瀏覽器。瀏覽器根本不知道服務器發送的內容從哪里來的,所以它的地址欄還是原來的地址。

  • redirect是服務端根據邏輯,發送一個狀態碼,告訴瀏覽器重新去請求那個地址,所以地址欄顯示的是新的URL。

 

  • forward轉發頁面和轉發到的頁面可以共享request里面的數據。  

  • redirect不能共享數據。 

 

  • redirect不僅可以重定向到當前應用程序的其他資源,還可以重定向到同一個站點上的其他應用程序中的資源,甚至是使用絕對URL重定向到其他站點的資源。     

  • forward只能在同一個Web應用程序內的資源之間轉發請求。

 

  • forward是服務器內部的一種操作。       

  • redirect是服務器通知客戶端,讓客戶端重新發起請求。

 

  • forward一般用於用戶登陸的時候根據角色轉發到相應的模塊。   

  • redirect一般用於用戶注銷登陸時返回主頁面和跳轉到其它的網站等。

     

  • forward效率高。     

  • redirect效率低。

 


 

redis常用的五種數據類型

 

1.String(字符串)

String是簡單的 key-value 鍵值對,value 不僅可以是 String,也可以是數字。它是Redis最基本的數據類型,一個redis中字符串value最多可以是512M。

 

2.Hash(哈希)

Redis hash 是一個鍵值對集合,對應Value內部實際就是一個HashMap,Hash特別適合用於存儲對象。

 

3.List(列表)

Redis 列表是簡單的字符串列表,按照插入順序排序。你可以添加一個元素導列表的頭部(左邊)或者尾部(右邊)。

底層實現為一個雙向鏈表,即可以支持反向查找和遍歷,更方便操作,不過帶來了部分額外的內存開銷,Redis內部的很多實現,包括發送緩沖隊列等也都是用的這個數據結構。

 

4.Set(集合)

Redis的Set是String類型的無序集合,它的內部實現是一個 value永遠為null的HashMap,實際就是通過計算hash的方式來快速排重的,這也是set能提供判斷一個成員是否在集合內的原因。

 

5.zset(有序集合)

Redis zset 和 set 一樣也是String類型元素的集合,且不允許重復的成員,不同的是每個元素都會關聯一個double類型的分數,用來排序。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM