Java后台面試之java基礎


經典類概念性問題

1.java支持的數據類型有哪些?什么是自動拆裝箱?

12.Java有哪些特性,舉個多態的例子。

14.請列舉你所知道的Object類的方法。

15.重載和重寫的區別?相同參數不同返回值能重載嗎?

23.接口和抽象類的區別是什么?

13.String為啥不可變?不可變的優勢在哪里?

16.”static”關鍵字是什么意思?Java中是否可以覆蓋(override)一個private或者是static的方法?

*為什么重寫equals還要重寫hashcode??

*JAVA 8的新特性

 

 

普通概念性問題

4.&和&&的區別?

5.你能比較一下Java和JavaSciprt嗎?

6.Java中是如何支持正則表達式操作的?

18.靜態變量存在哪?

19.講講什么是泛型? 

21.是否可以在static環境中訪問非static變量?

28.內部類可以引用他包含類的成員嗎?有沒有什么限制?

26.Static Nested Class 和 Inner Class的不同(內部類和靜態內部類)

30.Java中,什么是構造函數?什么是構造函數重載?什么是復制構造函數? 

70.java中有幾種類型的流?JDK為每種類型提供了一些抽象類以供繼承,請分別說出他們是哪些類?

71.什么是java序列化,如何實現java序列化?

 

反射

64.講講java中反射

22.談談如何通過反射創建對象?

 

異常

68.一般異常和運行時異常有何區別?

69.常見的運行時異常

27.JAVA語言如何進行異常處理,關鍵字:throws,throw,try,catch,finally分別代表什么意義?在try塊中可以拋出異常嗎?

*OOM異常的4種可能分析

 

 

關鍵字

2.int 和 Integer 有什么區別 ?

3.String 和StringBuffer以及StringBuilder的區別

10.講一講Java里面的final關鍵字怎么用的? 

20.解釋extends 和super

42.Iterator和ListIterator的區別是什么?

45.Collection 和 Collections的區別。

25.final, finally, finalize的區別

 

接口:

24.Comparable和Comparator接口是干什么的?列出它們的區別。

40.Java集合類框架的基本接口有哪些?

41.為什么集合類沒有實現Cloneable和Serializable接口?

 

多線程

9.鎖有了解嘛,說一下Synchronized和lock 

 62.鎖和同步的區別(Lock和Synchorized)

61.講一下synchorized,可重入怎么實現

7.介紹一下Synchronized鎖,如果用這個關鍵字修飾一個靜態方法,鎖住什么?如果修飾成員方法,鎖住什么?

 

8.介紹一下Volatile

43.快速失敗(fail-fast)和安全失敗(fail-safe)的區別是什么? 

49.創建線程的四種方法:

58.請說出你所知道的線程同步的方法

55.同步方法與同步代碼塊的區別

56.在監視器(Monitor)內部,是如何做線程同步的?程序應該做哪種級別的同步?

54.概括的解釋下線程的幾種可用狀態。

 

52.cyclicbarrier和countdownlatch的區別

53.java的模塊調用與回調機制

 

59.stop()和suspend()方法為何不推薦使用,請說明原因?

60.線程的sleep()方法和yield()方法有什么區別?

57.sleep和wait有什么區別?

11.wait方法底層原理 

 

*線程安全之CAS機制詳解(分析詳細,通俗易懂)

*JAVA體系的線程的實現,線程的調度,狀態的轉換

*Java線程安全與鎖優化

 線程池

50.Java中有哪些線程池?

51.線程池有什么好處?

46.線程,進程,然后線程創建有很大開銷,怎么優化?

47.線程池運行流程,參數,策略

48.講一下AQS

 

 

JAVA虛擬機

17.類加載機制,雙親委派模型,好處是什么?

65.CMS中的內存碎片問題如何解決?

66.類的加載機制以及加載過程內存圖

67.jvm最大內存限制多少?

*JVM有哪些分區?(解釋詳細 通俗易懂)

*HotSpot虛擬機對象探秘(對象創建,對象內存布局,對象訪問定位)

*JAVA 垃圾收集算法,垃圾收集器與內存分配策略(內容全面,解析簡單易懂)

*Java內存模型(和堆棧等不是同一層次的划分)

 

 

JAVA設計模式

29.談一下面向對象的"六原則一法則"

 

Map:

31.Map和ConcurrentHashMap的區別? 

32.hashMap內部具體如何實現的?

33.如果hashMap的key是一個自定義的類,怎么辦?

34.HashMap底層,負載因子,為啥是2^n?

35.ConcurrentHashMap鎖加在了哪些地方?

36.concurrenthashmap有啥優勢,1.7,1.8區別?

39.什么是TreeMap?TreeMap的底層?

 *JAVA中map的分類和各自的特性

 

 

List:

37.ArrayList和LinkedList的區別,如果一直在list的尾部添加元素,用哪個效率高?

38.ArrayList是否會越界?

44.ArrayList,Vector,LinkedList的存儲性能和特性是什么?

 

1.java支持的數據類型有哪些?什么是自動拆裝箱?

基本數據類型:

整型:byte short int long

字符型:char

浮點型:float double

布爾型:boolean

整數默認int,小數默認double,float和long必須加后綴(因為默認double,轉float會損失精度,所以不會自動轉,如果超過了int范圍沒有加L后綴,那么也會報錯)

String類型屬於引用類型,引用類型聲明的變量是指該變量在內存中實際存儲的是一個引用地址,實體在堆中,引用類型包括類,數組,接口等,而包裝類也屬於引用類型,自動裝箱,拆箱就是基本類型和引用類型(此處為包裝類型)間的轉換,而轉換為引用類型的話,就new一個對象,從而可以調用包裝類型中封裝好的方法,方便使用!

 

2.int 和 Integer 有什么區別 ?

1.int是基本數據類型,Integer的引用數據類型

2.int默認0,Integer默認null

3.int存儲在棧中,Integer存儲在堆中

4.int變量存儲數據原始值,Integer變量存儲的是引用地址

5.Integer通過實例化創建的,是對象!

 

3.String 和StringBuffer以及StringBuilder的區別

String 字符串常量

StringBuffer 字符串變量(線程安全)

StringBuilder 字符串變量(線程不安全)

字符串常量創建后長度,內容都不可以改變,可以new,也可以直接“abc”這樣給值

字符串變量倍創建后長度,內容都是可以被修改的,只能通過new創建!

 

4.&和&&的區別?

&&和&都是判斷兩邊表達式是否為真,使用&時,如果左邊為假,那么它還會去驗證右邊,但是使用&&時,如果左邊為假,它就不會驗證右邊了,具有短路的效果,效率比單&高!

 

5.你能比較一下Java和JavaSciprt嗎?

java:面向對象 需要編譯再進行運行 屬於強類型(編譯時才能確定變量類型)

javascript:基於對象和事件驅動 解釋型語言 弱類型(執行時才能確定變量類型)

 

6.Java中是如何支持正則表達式操作的?

通過String類中的一些方法,比如matchs(),replaceAll(),replaceFirst(),split()等

 

7.介紹一下Synchronized鎖,如果用這個關鍵字修飾一個靜態方法,鎖住什么?如果修飾成員方法,鎖住什么?

Syscronized鎖是同步鎖,如果關鍵字修飾靜態方法的話是一個類鎖(當前類的所有線程都必須等待同步線程執行),如果關鍵字修飾成員方法的話是一個對象鎖(當前對象的所有進程必須等待同步進程執行完,釋放鎖)

 (靜態方法:可以不用生成實例對象而直接引用)

 

8.介紹一下Volatile

Volatile關鍵字修飾共享變量,保證其他線程訪問這個變量的時候始終是最新值,也就是Volatile會更新最新值到java主內存中,其他線程使用這個變量的時候會從java主內存中去取得這個變量(非volatiel不具備這個特性,非volatile變量在被某個線程修改之后會被緩存,線程A更新了這個值,線程B讀取到的可能並不是最新值),volatile不具備原子性(讀volatile具備原子性,但volatile變量i的i++操作不具備原子性),這是volatile與synchrozied,lock的最大差異!

 

9.鎖有了解嘛,說一下Synchronized和lock

java為某個共享資源的同步提供了兩種鎖機制:Synchrozied和lock

 

二者有以下區別:

 

1.用法不一樣:在需要同步的對象中加入Synchrozied鎖,Synchrozied既可以加載在方法的前面也可以加載在特定的代碼塊中,括號表示需要鎖的對象,而lock需要顯示的指定起始位置和終止位置,Synchrozied是托管給JVM執行的,而lock的鎖定是通過開發人員手動代碼實現的

 

2.性能不一樣:jdk5中增加了一個lock接口的實現類ReentrantLock,他們的性能在資源不同的情況下會有很大的不同:在資源競爭不是很激烈的情況下,synchorized的性能要優於ReentrantLock,但是在資源競爭很激烈的情況下,synchoorized的性能會下降很快而ReentrantLock的性能會基本保持不變

 

3.鎖機制不一樣:synchorized獲得鎖和釋放鎖的方式都在結構中,當獲取多個鎖時間,必須以相反的方式釋放鎖,並且自動解鎖,不會應用出現異常而引發死鎖,而Lock則需要開發人員手動釋放,並且必須在final塊中釋放,否則會引發死鎖

 

4.靈活性不一樣:比如ABC三個線程,兩個讀文件一個寫文件,synchorized只能依次枷鎖和解鎖,而lock可以讓讀共享,這樣更好,所以后面就引發了鎖優化技術

 

10.講一講Java里面的final關鍵字怎么用的?

被final修飾的類不能繼承,被final修飾的fan方法不能重寫,被final修飾的變量為常量,值不能改變

 

11.wait方法底層原理

 object中的方法,可以暫停線程,期間會釋放對象鎖,不像sleep方法,線程休眠期間依然持有鎖,wait方法的線程必須調用notify或notifyall方法喚醒線程

 

比如:當一個線程執行到wait方法時,它就進入到一個和對象相關的等待池中,同時失去對象鎖,當它被一個notify方法喚醒時,等待池中的線程就被放到了鎖池中,該線程從鎖池獲得對象鎖,然后回到wait前的中斷現場

 

12.Java有哪些特性,舉個多態的例子。

 封裝,繼承,多態,多態可以理解為一致類型,不同形態,比如Animal animal=new Dog()

 

13.String為啥不可變?不可變的優勢在哪里?

 

因為String類被final修飾,string類的底層數組也是被final修飾的

 

1.提高字符串常量池的效率和安全,如果你知道一個對象是不可變的,那么拷貝對象的內容時就不用復制它本身而只用復制它的地址,復制地址需要很小的內存,效率也很好

 

2.對多線程安全,多線程的情況下,一個可變對象的值可能會被多個線程修改造成不可預期的結果,而不可變對象就不存在這個問題

 

14.請列舉你所知道的Object類的方法。

1.clone:創建並返回此對象的一個副本

 

2.equals:比較“相等”

 

3.finalize:當垃圾回收器確定不存在該對象的引用時,由對象的垃圾回收器調用方法

 

4.getclass:返回它的運行時類

 

5.hashcode:返回對象的哈希碼

 

6.notify:喚醒此對象監視器上等待的單個線程(隨機喚醒)

 

7.notifyall:換線此對象監視器上等待的所有線程

 

8.tostring:返回對象的字符串表示

 

9.wait:暫停當前線程(可以設置超時時間)

 

15.重載和重寫的區別?相同參數不同返回值能重載嗎?

重寫是針對父類和子類的概念,重載是針對一個類中的概念,相同參數不同返回值不可以重載,因為重載必須改變參數列表,否則虛擬機怎么知道要調用哪一個

 

16.”static”關鍵字是什么意思?Java中是否可以覆蓋(override)一個private或者是static的方法?

static關鍵字表明一個成員變量或者成員方法可以在沒有所屬類的實例變量的情況下被訪問

 

java中static方法不能被覆蓋,因為方法的覆蓋是基於運行時動態綁定的,而static方法是編譯時靜態綁定的,static方法跟類的任何實例都不相關,所以沒有覆蓋這個概念

 

java中也不可以覆蓋private方法,因為private修飾的變量和方法只能在當前類中使用,任何其他類繼承了該類是訪問不到private變量和方法的

 

17.類加載機制,雙親委派模型,好處是什么?

類加載機制:JVM把類的數據從class文件加載到內存,並對數據進行校驗,轉換解析和初始化,最終形成可以被JVM直接使用的java類型

 

雙親委派模型:每次收到類的加載請求時,先將請求委派給父類加載器,如果父類加載器無法完成加載,那么子類嘗試自己加載,這樣使得java類隨着類加載器一起具備了一種帶有優先級的層次關系

 

18.靜態變量存在哪?

static修飾的,位於全局區

 

19.講講什么是泛型?

泛型是一種參數化的類型,它的<>里面可以放任何類型,而且不需要強制類型轉化,是多態的一種表現

 

20.解釋extends 和super 泛型限定符-上界不存下界不取

extends指定上界限,只能傳入本類和子類

 

super指定下界限,只能傳入本類和父類

 

21.是否可以在static環境中訪問非static變量?

不可以,因為靜態的成員屬於類,隨着類的加載而加載到靜態方法區內存,當內加載時,此時不一定有實例被創建,沒有實例,就不能訪問非靜態的成員

 

22.談談如何通過反射創建對象?

1.通過class字節碼對象newInstance()(默認通過無參構造創建)

2.通過獲取構造器(有參構造器)

 

23.接口和抽象類的區別是什么?

抽象類是對類的抽象,是一種模板設計,接口是行為的抽象,是一種行為規范(接口只能定義一系列方法算是定義行為,而不能包含具體的變量,不能擁有自己的屬性,但是抽象類能夠擁有)

接口是公開的,里面不能有私有的方法或變量,是讓別人使用的,而抽象類可以有私有的方法或者變量,另外,實現接口的一定要實現接口定義的所有方法,而實現抽象類可以有選擇的重寫需要用得到的方法,一般應用開發,最頂級的是接口,然后是抽象類實現接口,最后才到具體類,還要接口可以實現多重繼承,而一個類只能繼承一個父類,但可以通過多個接口實現多重繼承

 

24.Comparable和Comparator接口是干什么的?列出它們的區別。

都是比較器,用來實現對象的排序比較

 

comparable是內部比較比較器,簡單的說就是把比較器寫在類的內部,具體操作就是類實現comparable接口,然后重寫compareto方法

 

comparato是外部比較器,簡單的說就是把比較器寫在類的外部(沒錯,就是在外邊重新定義了一個比較器類),具體操作就是實現comparator接口,重寫compare方法

 

內部比較器compareable比較符合java封裝的思想,高內聚,但是!外部比較器比內部比較器靈活,更加容易維護!

 

25.final, finally, finalize的區別

final:用來定義變量,方法,參數,類,表示不可更改類型

finally:在try/catch語句中使用,附帶一個語句塊,表示最后執行

finalize:object的方法,垃圾回收器操作機制中的一部分,進行垃圾回收操作時會調用finalize,看重寫這個方法,在這里實現釋放系統資源等操作

 

26.Static Nested Class 和 Inner Class的不同

static nested class:靜態內部類

inner class:內部類

靜態內部類static nested class可以不依賴外部類的實例被實例化,而通常的內部類需要在外部類實例化之后才能實例化

內部類可以在兩個地方定義:1.定義方法的地方,2.方法內

 

27.JAVA語言如何進行異常處理,關鍵字:throws,throw,try,catch,finally分別代表什么意義?在try塊中可以拋出異常嗎?

try:將可能發生異常的語句監視起來

catch:捕獲到了異常就進行里面的操作

finall:不管是否有異常最后都會執行的語句

throws:拋出的異常的聲明關鍵字

throw:拋出異常的動作的關鍵字

try塊中不能拋出異常

 

28.內部類可以引用他包含類的成員嗎?有沒有什么限制?

如果不是靜態內部類的話就可以,沒有什么限制

四種內部類:

1.靜態內部類:static修飾,只能訪問外部類中被static修飾的成員變量或方法

2.成員內部類:最普通的內部類,可以無條件訪問外部類的屬性和方法

3.局部內部類:定義在外部類的方法中的,可以訪問外部類的所有變量和方法,但是不能隨便訪問該方法里面的局部變量,除非該變量被final修飾

4.匿名內部類

 

29.談一下面向對象的"六原則一法則"

https://www.cnblogs.com/yinbiao/p/10535976.html

 

30.Java中,什么是構造函數?什么是構造函數重載?什么是復制構造函數?

java中的構造函數是為了初始化對象的,構造函數的函數名和類名一致,默認的構造函數是沒有參數,沒有返回值的,且沒有內容,構造函數的重載就是函數名和類名相同,參數類型不同,參數不同,同樣也是初始化對象,java中沒有復制構造函數的概念

 

31.Map和ConcurrentHashMap的區別?

Map是接口,concurrentHashMap是線程安全的map,具有同步能力

 

32.hashMap內部具體如何實現的?

HashMap是數組加鏈表實現的,數組部分是主體,數組中的每一個部分可以是鏈表點,鏈表主要是為了解決hash沖突,HashMap根據鍵生成hash值,根據hash值存放鍵對應的值,而有時候可能兩個不同的鍵hash值是相同的,會產生hash沖突,這個時候就需要采用鏈表存放產生hash沖突的值了

 

33.如果hashMap的key是一個自定義的類,怎么辦?

重寫hashcode,最大程度避免產生hash沖突,還要重寫equals

 

34.HashMap底層,負載因子,為啥是2^n?

hashcode%length=鍵對應的值存放的位置

 

length:HashMap的容量=2^n

 

h:插入元素的hashcode

 

如果lenght為2^n,則轉化為2進制必定是1111……的形式,這樣在插入元素的hashcode與操作的時候效率會非常高,而且空間不浪費,如果length不是2^n,比如lenght=15,則length-1=14,對應的二進制為1110,那么與出的結果肯定是最后一位都為0,那么最后一位都為1的位置都被浪費了,比如0001,0101等等,這樣使得空間浪費,增加了碰撞的概率

 

 所以:當length=2^n時,h&(length-1)==h%length

 

35.ConcurrentHashMap鎖加在了哪些地方?

HashTable是通過synchoronized來保證線程安全的,在總體上只加了一個鎖,這樣導致多線程時HashTable效率低下,一位當一個線程訪問hashtable的同步方法時,其他線程要訪問hashtable的同步方法會陷入阻塞,比如線程1使用put添加元素,線程2不能不能put添加元素,並且不能使用get獲取元素,所以效率低下,原因就是HashTable只有一個整體鎖,鎖的粒讀太大

 

ConcurrentHashMap使用分段鎖技術,對每個段進行加鎖,當線程1put元素時,並不是對整個HashMap進行加鎖,而是通過hashcode值確定要將新值放入哪個分段,然后對那分段進行加鎖,線程2只要pu的元素不是和線程1put的元素處於同一個分段,就實現了真正的並行插入!!(默認16個段)

 

細化鎖的粒讀!!!(分段鎖技術)

 

ConcurrentHashMap結構圖

 

一個segment元素代表一個分段鎖

 

 

36.concurrenthashmap有啥優勢,1.7,1.8區別?

在多線程環境下,使用HashMap進行put操作時存在數據丟失的情況,為了避免這種情況,可以使用多線程安全的concurrenthashmap代替HahsMap

在1.7下,concurrenthashmap采用segment+HashEntry實現,即分段鎖+哈希表實現,哈希表是采用哈希數組和鏈表實現的,值得探討的是concurrenthashmap的size方法的實現,計算元素的個數是個有趣的問題尤其是在多線程的環境下,因為你不知道你計算size的時候,是否有新的元素插入進來,JDK1.7對解決這個問題有兩種解決方案:

 

第一個是不使用加鎖的模式嘗試多次去計算currenthashmap的size,最多三次,比較前后計算結果是否不同,結果一致就認為當前沒有元素加入,計算結果是准確的,

 

第二種方法是如果第一種方法不符合,計就給每個segment加上鎖,然后計算currentmap的size。

 

在1.8下,其已經擯棄了segment段的概念,而是直接用node數組+鏈表+紅黑樹的數據結構來實現,並發控制使用synchorized和CAS來操作,整個看起來就像是優化過且線程安全的HashMap,雖然在1.8中還能看到segment這種數據結構,但是已經簡化了屬性,只是為了兼容舊的版本,concurrenthashmap的初始化其實是一個空操作,這也是跟其他集合類有區別的地方,concurrenthashmap的初始化操作不是在構造函數實現的,而是在put操作中實現的,同時使用了紅黑樹來優化,因為基於長度很長的鏈表的遍歷是一個很長的過程,而紅黑樹遍歷的效率是很快的,代替一定閥值的鏈表,這是最佳的拍檔

 

37.ArrayList和LinkedList的區別,如果一直在list的尾部添加元素,用哪個效率高?

Arraylist:可以理解為動態數組,底層實現仍是數組

LinkedList:單向鏈表

當一直在list尾部增加元素時,讀寫效率都是ArrayList高,但是當在指定位置插入的時候(最后情況每次都是插入在第一個的前面),LinkedList效率高

 

38.ArrayList是否會越界?

會越界,雖然ArrayList的底層是動態數組,但是在多線程的環境下,擴容的時候往往會發生越界,比如這樣一種情況:

 

用第一次異常,即下標為15時的異常舉例。當集合中已經添加了14個元素時,一個線程率先進入add()方法,在執行ensureCapacityInternal(size + 1)時,發現還可以添加一個元素,故數組沒有擴容,但隨后該線程被阻塞在此處。接着另一線程進入add()方法,執行ensureCapacityInternal(size + 1),由於前一個線程並沒有添加元素,故size依然為14,依然不需要擴容,所以該線程就開始添加元素,使得size++,變為15,數組已經滿了。而剛剛阻塞在elementData[size++] = e;語句之前的線程開始執行,它要在集合中添加第16個元素,而數組容量只有15個,所以就發生了數組下標越界異常!

 

39.什么是TreeMap?TreeMap的底層?
實現了sortMap接口,能夠把保持的記錄按照鍵排序(默認升序),也可以知道排序比較器,遍歷時得到的數據是排過序的

 紅黑樹

 

40.Java集合類框架的基本接口有哪些?

總共有兩大接口,collection和Map,一個元素集合,一個鍵值對集合,其中List和Set接口繼承了Collection接口,而ArrayList和LiskList實現了List接口,HashSet實現了Set接口,HashMap和HashTable實現了Map接口,並且HashTable是線程安全的,但是HashMap性能更好

Collecttion接口:

     List接口:ArrayList,LinkList,Vector(Stack繼承了Vector接口)

     Set接口:HashSet,SortedSet(TreeSet)

Map接口:

      SortedMap(TreeMap)

      HashTable(LinkedHashMap)

      WeakHashMap

 

41.為什么集合類沒有實現Cloneable和Serializable接口?

Cloneable接口是用於淺克隆,而Serializable接口則是用於深克隆,標識性接口,之所以用到克隆是因為有時需要把對象信息保存到本地磁盤,防止在傳輸時出現亂序,而容器沒有那個必要,只是用來存儲數據而已

 

42.Iterator和ListIterator的區別是什么?

 

43.快速失敗(fail-fast)和安全失敗(fail-safe)的區別是什么?

快速失敗:當迭代器遍歷集合對象時,如果對集合對象的內容進行的修改,那么就會發生快速失敗

原理:迭代器在遍歷時直接訪問集合中的內容,並且在遍歷過程中使用一個modcount變量,集合在遍歷期間如果內容發生變化,就會改變modcount的值,每當迭代器使用hashnext遍歷下一元素之前,都會檢測一下modcount的值,是的話返回遍歷結果,不是的話拋出異常,終止遍歷

快速失敗不能在多線程下並發使用!

安全失敗:采用安全失敗機制的集合容器,在遍歷時不能直接在集合的內容上訪問,而是先復制原有的集合內容,在拷貝的集合上進行遍歷,由於是對拷貝的進行遍歷,所以在遍歷過程中修改副本集合元素並不能被原集合檢測到

安全失敗可以在多線程先並發使用!

 

44.ArrayList,Vector,LinkedList的存儲性能和特性是什么?
Vector使用了sychronized方法(線程安全),所以在性能上比ArrayList要差些.

LinkedList使用雙向鏈表方式存儲數據,按序號索引數據需要前向或后向遍歷數據,所以索引數據慢,是插入數據時只需要記錄前后項即可,所以插入的速度快。

 

45.Collection 和 Collections的區別。

Collection是接口,各自集合結構的父接口,繼承它的接口主要有set,List,提供關於集合的一些操作

Collections是類,針對集合類的工具類,提供一系列的靜態方法實現對各種集合的排序,搜索和線程安全

 

46.線程,進程,然后線程創建有很大開銷,怎么優化?

使用線程池(任務的提交和執行解耦)

優點:

1)降低資源消耗:通過重復利用已創建的線程來降低線程創建和銷毀造成的消耗

2)提高響應速度:當任務到達時,任務可以不需要等到線程創建就能立即執行

3)提高線程的可管理性:線程是稀缺資源,如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配,調優和監控

 

47.線程池運行流程,參數,策略

線程池的運行流程:

線程池接受提交的任務,判斷有沒有空閑線程,有的話直接使用空閑線程執行任務,沒有空閑線程的話,再判斷當前線程數是否小於核心線程數,如果當前線程數小於核心線程數就創建一個新線程去執行該任務,如果線程數大於或等於核心線程數,就判斷阻塞隊列是否已滿,阻塞隊列沒有滿的話將任務加入隊列等待執行,若阻塞隊列滿了的話,則判斷當前線程數和最大線程數的關系,若大於等於則拒絕任務,若小於則創建新線程執行任務

這里寫圖片描述

線程池的參數:

1.corePoolSize:核心線程數

  *核心線程會一直存活

  *當線程數小於核心線程數時,即使有空閑線程,線程池也會優先創建新線程處理任務

  *通過設置參數,核心線程也可以空閑超時關閉

2.queueCapacity:阻塞隊列

  *當核心線程數達到最大時,新任務會放在阻塞隊列中等待

3.maxPoolsize:最大線程數

  *當線程數>=核心線程數且阻塞隊列已滿,線程池會創建新線程來處理任務

  *當前線程數=maxPoolSize且阻塞隊列已滿,線程池會拒絕任務拋出異常

4.keepAliveTime:線程空閑時間

  *當線程空閑時間達到keepAliveTime時,線程會退出,直到線程數量=核心線程數

5.rejectedExecytionHandler:任務拒絕處理器

  *兩種情況會拒絕任務

    *當前線程數已經達到最大線程數,阻塞隊列已滿,會拒絕新任務

    *當線程池被調用shutdown后,會等待線程池里面的任務執行完畢再shutdown,如果在調用shutdown和線程池真正shutdown之間提交任務,會拒絕新任務

線程池的拒絕策略(4種):

1.丟棄任務並且拋出異常

2.丟棄任務不拋出異常

3.丟棄隊列最前面的任務,然后嘗試重新執行任務

4.由調用線程處理該任務

 

48.講一下AQS

AQS:抽象的隊列式同步器,是除了java自帶的關鍵字之外的鎖機制

AQS的核心思想:如果被請求的共享資源空閑,則將當前請求資源的線程設置為有效線程,並將共享資源設置設置為鎖定狀態,如果被請求的共享資源被占用,那么就需要一套線程阻塞等待以及被喚醒時分鎖分配的機制,這個機制就是AQS,AQS是用CLH自旋隊列實現的,即將暫時不獲取鎖的線程加入到隊列中

AQS是將每一條請求共享資源的線程封裝成一個CLH鎖隊列的階段,來實現鎖的分配

用大白話說就是AQS是基於CLH隊列,用volatile修飾共享變量state,線程通過CAS去改變狀態符,成功則獲取鎖,失敗則進入等待隊列,等待被喚醒

在這里插入圖片描述

AQS定義了兩種資源共享方式:

  *獨占:只能一個線程執行

  *共享:多個線程可以同時執行

 

49.創建線程的四種方法:

1)繼承類:繼承Thread類,重寫run方法

2)實現接口:實現Runnable接口,重寫run方法

3)實現接口:實現Callable接口,重寫run方法

4)使用Executor框架創建線程

一般使用實現Runnable接口的方法,因為一個類可以實現多個接口

 

50.Java中有哪些線程池?

1.newCachedThreadPool:線程數量不定的可緩存線程池,最大線程數是一個很大的值,空閑線程超時60秒被回收,該線程池比較適合大量但是耗時少的任務

2.newFixedThreadPool:指定工作線程數量的線程池,每當提交一個任務就創建一個工作線程,當工作線程數量到達線程池初始最大數,則將任務存入阻塞隊列中,具有線程池提供程序效率和創建線程所耗開銷的優點,但是在線程空閑時,即線程池中沒有最終運行的任務,它也不會釋放線程,會占用一定的系統資源

3.newSingleThreadExecutor:單線程化的Excetor,只創建唯一的工作線程來執行任務,保證所有任務按照FIFO執行,如果這個線程異常結束,會有另外一個線程取代它

4.newScheduleThreadPool:定長線程池,核心線程數量固定,而非核心線程數是沒有限制的,並且當非核心線程閑置時間會被立刻回收,可安排給定延遲后運行命令或者定期執行,這類線程池主要用於執行定時任務和具有固定周期的重復任務

 

51.線程池有什么好處?

1)降低資源消耗:通過重復利用已經創建的線程來降低線程創建和銷毀造成的消耗

2)提高響應速度:當任務到達時,任務可以不需要等待線程創建就能立即執行

3)提高線程的可管理性:線程是稀缺資源,如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使線程池可以進行統一的分配,調優和監控

 

52.cyclicbarrier和countdownlatch的區別

CountDownLatch:同步輔助類,允許一個線程或多個線程,等待其他一組線程完成操作,再繼續執行,可以理解成倒計時鎖,形象比喻:我們在玩LOL的時候會出現十個人不同的加載狀態,但是最后一個人由於各種原因始終加載不了100%,於是系統自動等待玩家都加載為100%,才展現游戲畫面

CyclicBarrier:可以看成是個障礙,所有的線程必須到齊后才能一起通過這個障礙,CyclicBarrier就是一個柵欄,等待所有線程到達后再執行相關操作

 

53.java的模塊調用與回調機制

java模塊調用可以分為3個部分:同步調用,異步調用,回調

同步調用:類A的方法a調用類B的方法b,缺點就是如果類B的方法b在調用時阻塞了的話,類A的方法a剩余的代碼無法繼續執行下去

異步調用:類A的方法a通過新建線程的方式調用類B的方法b,就算類B的方法b阻塞了,類A的方法a也可以繼續執行

回調:類A的a方法調用類B的b方法,類B的b方法執行完畢主動調用類A的callback方法,回調又分為同步回調和異步回調,同步回調意味着需要等待,異步回調意味着不需要等待

 

54.概括的解釋下線程的幾種可用狀態。

1)新建

2)就緒(可運行)

3)運行中

4)阻塞:等待阻塞(wait方法),同步阻塞(鎖機制),其他阻塞(sleep方法,join方法,IO請求)

5)死亡

 

55.同步方法與同步代碼塊的區別

同步方法:修飾一般方法鎖住的是當前對象的該方法,修飾靜態方法是把當前類的所有實例對象都鎖住了

同步代碼塊:指定獲得哪個對象上的鎖,鎖住的是當前對象的某個代碼塊

兩者的本質都是同步鎖,不過是鎖的粒度大小有區別,同步方法的鎖粒度大,同步代碼塊的鎖粒度小,同步代碼塊比同步方法好,因為我們一般都只需要對臨界區進行同步,鎖粒讀越小,性能越高,而且同步代碼塊中我們可以自由的選擇鎖

 

56.在監視器(Monitor)內部,是如何做線程同步的?程序應該做哪種級別的同步?

監視器Monitor是一個同步工具,相當於操作系統中的互斥量,即值為1的信號量,它內置與每一個Object對象中,相當於一個許可證,拿到許可證就可以進行操作,沒有拿到則需要阻塞等待

 

57.sleep和wait有什么區別?

1.來源不同:sleep來自Thread類,而wait來自Object類,sleep是Thread的靜態類方法,誰調用誰去睡覺,即使在線程a里面調用了b的sleep方法,還是線程a去睡覺,要讓b去睡覺,得在b的代碼中調用sleep

2.對資源的態度不同:sleep方法讓線程去睡覺沒有釋放鎖,沒有讓出資源,而wait釋放了鎖,進入線程等待池等待,一般wait不會加時間限制,因為如果wait運行的資源不夠,出來也沒有用,要等待其他線程調用notify或者allnotify方法喚醒等待池中的線程,才能進入就緒隊列等待OS分配資源,sleep可以用指定的時間喚醒,如果時間沒有到只能調用interrupt強行打斷(Thread.sleep(0)的目的是觸發操作系統重新進行一次CPU競爭

3.使用范圍:sleep可以在任何地方使用,而wait只能在同步方法或者同步塊中使用

4.異常處理:sleep必須捕獲異常,而wait不用

 

58.請說出你所知道的線程同步的方法

1)synchotized同步方法或者同步方法塊

2)wait和sleep

3)volatile變量

  *每次volatile變量被修改后都會重新寫入內存

  *volatile變量為域變量的訪問提供了一套免鎖機制

  *使用volatile修飾相當於告訴JVM該變量可能會被其他線程更新

  *因此使用該變量每次都是重新從內存里面拿值,而不是拿寄存器里面的原來的值

  *volatile不提供任何原子操作,它也不能用來修飾final類型的變量

4)使用可重入鎖,ReenRantLock類,提供了可重入,互斥,實現了LOck接口的鎖

5)局部變量 ThreadLocal類關聯變量,則每一個使用該變量的線程都獲得該變量的副本,副本之間相互獨立,這樣每一個線程都可以隨意改變自己的變量副本,而不會對其他線程產生影響

6)阻塞隊列實現線程同步

7)使用原子變量實現線程同步:AtomicInteger類

 

59.stop()和suspend()方法為何不推薦使用,請說明原因?

stop會導致不安全,如果在同步塊執行一半時,stop來了,后面還沒有執行完呢,鎖沒了,線程退出了,別的線程又可以操作你的數據了,所以不安全

suspend會導致死鎖,因為掛起后是不釋放鎖的,別人也就阻塞着,如果沒有人喚醒,那就一直死鎖

 

60.線程的sleep()方法和yield()方法有什么區別?

1)sleep方法給其他線程運行機會時不會考慮線程的優先級,因此會給低優先級的線程以運行的機會,yield方法只會給相同優先級或者更高優先級的線程機會

2)線程執行sleep后進入阻塞狀態,而執行yield后進入就緒狀態

3)sleep方法聲明拋出InterruptedExcetion,而yield方法沒有聲明任何異常

 

61.講一下synchorized,可重入怎么實現

對象監視器,會在對象頭部有個區域,專門記錄鎖的信息,包括持續線程的鎖,鎖的計數器,鎖的狀態這些,線程在嘗試獲取對象鎖時,先看看鎖計數器是否為0,為0說明鎖還在可以獲取鎖,於是獲取鎖,鎖計數器變成1,並記錄下持有鎖的線程,當有線程再來請求時,先看看是不是當前持有鎖的線程,是的那就直接訪問,鎖計數器+1,如果不是,直接阻塞,當退出時,計數器-1,變成0時,釋放鎖

 

62.鎖和同步的區別(Lock和Synchorized)

1)Lock是一個接口,而synchorized是一個關鍵字

2)synchorized是內置語言實現的,是在JVM的層面實現的,可以通過工具進行監控,在dai嗎異常時,JVM還會自動釋鎖定,而Lock不行,lock是通過代碼實現的,只能程序員手動unlock,unlock必須寫在finally{}中

3)Lock可以讓等待的線程響應中斷,線程可以去做其他事情,Synchorized不行,等待的線程會一直等待下去,不能去做其他事情

4)Lock可以知道是否獲取到鎖,知道鎖的狀態,而synchronized卻無法辦到

5)Lock可以提高多個線程進行讀操作的效率

 

63.Java中的LongAdder和AtomicLong有什么區別?

AtomicLong的原理是依靠底層的CAS來保障原子性的更新數據,在要添加或者減少的時候會使用自循環(CLH)的方式不斷的CAS到特定的值,從而達到更新數據的目的,然而在線程競爭激烈的情況下,自循環往往浪費很大的資源

所以面對AtomicLong的這個缺點,有了LongAdder的誕生,主要思想就是把一個數拆分成多個數的和,修改的時候只修改其中一個數,這樣沖突的概率減少很多,有人說,base和數組中的值一直在該,會不准,不過這個沒有關系,高並發的時候也不可能准,反正看到的都是瞬時的

 

64.講講java中反射

1.反射機制的主要功能:

在運行時構造一個類的對象,判斷一個類所具有的成員變量和方法,調用一個對象的方法,生成動態代理,反射最大的應用就是框架

2.反射的應用:

Spring的IOC/di

JavaBean和jsp之間的調用

JDBC的classForName

3.反射的缺點:性能問題,大量使用反射會使得系統性能大打折扣

4.基本反射功能的實現:

獲取class對象:

  *獲取class類的ForName靜態方法

  *直接獲取某一個對象的class

  *調用某個對象的getClass方法

判斷是否為某個類的實例:instanceof關鍵字判斷是否為某個類的實例

創建實例:

  *使用Class對象的newInstance方法來創建CLass對象對應類的實例

  *先通過Class對象獲取指定的Consructor對象,再調用Constructor對象的newInstance方法創建實例

 

65.CMS中的內存碎片問題如何解決?

  *增大Xmx(程序最大可以從操作系統獲取的內存數量)或者減少Xmn(年輕代大小)

  *在應用訪問最低的時候,在程序中主動調用system.gc(),比如每天凌晨

  *在應用啟動並完成所有初始化工作后,主動調用system.gc(),它可以將初始化數據壓縮到一個單獨的塊中,以騰出更多的連續空間給新生代晉升使用

  *降低XX:CMSInitiatingOccupancyFraction參數以提早執行垃圾回收,雖然不會進行內存碎片的壓縮整理,但是它會合並老年代中相鄰的free空間,這樣可以容納更多的新生代晉升行為

-XX:CMSInitiatingOccupancyFraction=70
CMS垃圾收集器,當老年代達到70%時,觸發CMS垃圾回收。

 

66.類的加載機制以及加載過程內存圖

前言:一個java文件從解碼到運行一般包括兩個過程:編譯和運行,編譯就通過javac命令編譯成字節碼文件,運行就是把class文件交給JVM執行,而我們所說的類加載過程就是JVM把字節碼文件中類的信息加載進內存,並解析生成相應class對象的過程,JVM不是一開始就是把所有的類都加載進內存,而且只有第一次遇到某個需要運行的類才會加載而且只加載一次

類的加載過程:

1)加載:把字節碼文件通過各個來源通過類加載器加載進入內存

  *字節碼來源:本地編譯生成的字節碼文件,jar包中的字節碼文件,遠程網絡得到的字節碼文件,以及動態代理實時編譯的字節碼文件

  *類加載器:啟動類加載器,擴展類加載器,應用類加載器,自定義類加載器

  *為什么會有自定義類加載器?java代碼容易被反編譯,如果要對自己的代碼加密的話,可以對編譯后的代碼進行加密,然后通過實現自己的自定義類加載器進行解密,最后再加載

2)驗證:保證加載進來的字節流符合JVM規范,不會造成安全錯誤

  *對文件格式的驗證,比如常量中是否有不被支持的常量

  *元數據的驗證:類是否繼承了被final修飾的類,類中的字段方法是否域父類沖突

  *字節碼驗證:保證程序語義的合理性

  *對符合引用的驗證:比如檢驗符號引用中通過全限定名是否能夠找到對應的類

3)准備:為類變量(不是實例變量)分配內存,賦予初值(8中類型基本數據默認0)

4)解析:將常量池符號引用替換為直接引用,舉例說明:現在調用方法a,方法a的地址是1234567,那么a就是符號引用,1234567就是直接引用

5)初始化:對類變量初始化,是執行類構造器的過程,換句話說就是指對static修飾的變量或者語句進行初始化,如果初始化一個類,其父類尚未初始化,則優先初始化其父類,如果同時包含多個靜態變量和代碼塊,則按照自上而下的順序依次執行

類加載過程內存圖:

 

 

67.jvm最大內存限制多少?

這個如果不使用-xx:Xmx -xx:Xms -xx:Permsize -xx:MaxPermsize參數進行設置的話,應該和不同版本的jdk的jvm最大內存限制相關吧。

公司 JVM版本                  最大內存(兆)client    最大內存(兆)server

SUN 1.5.x                          1492                            1520

SUN 1.5.5(Linux)                    2634                            2660

SUN 1.4.2                          1564                            1564

SUN 1.4.2(Linux)                    1900                            1260

IBM 1.4.2(Linux)                    2047                             N/A

BEA JRockit 1.5 (U3)                 1909                             1902

 

68.一般異常和運行時異常有何區別?

1.定義不同

運行時異常都是RunTimeException類及其子類,一般異常是RunTimeException以外的異常,都是Exception的子類

2.處理方法不同

運行時異常是不檢查異常,程序可以捕獲處理也可以不處理,一般異常必須進行捕獲處理,否則不能編譯通過

3.發生原因不同

運行時異常一般由程序邏輯錯誤引起

 

69.常見的運行時異常

空指針異常:NullPointerException

類轉換異常:ClassCastException

算術異常:ArithmeticExcepion

索引超出異常:IndexOutOfBoundsException

非法數據異常:LLLegalArgumentException

 

70.java中有幾種類型的流?JDK為每種類型提供了一些抽象類以供繼承,請分別說出他們是哪些類?

字節流,字符流

四種抽象類:字節流:InputStream,OutputStream

     字符流:InputStreamReader,OutputStreamWriter

 

71.什么是java序列化,如何實現java序列化?

java序列化是一種處理java對象的流機制,即是將對象進行流化,流化后的對象可以進行讀寫操作,可以保持於本地,也可以用於網絡傳輸

要實現java序列化,需要被序列化的類實現Serializable,通過使用輸出流(如FileOutputStream),可以構造對象輸出流,並使用其writeObject方法將對象寫出,可以通過構造對象輸入流接收對象

 


免責聲明!

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



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