某群入群面試題

1,什么是線程安全 (參考書:https://book.douban.com/subject/10484692/
2,都說String是不可變的,為什么我可以這樣做呢
String a = "1";
a = "2";
3,HashMap的實現(xiàn)原理
4,寫出三種單例模式,如果能考慮線程安全最好
5,ArrayList和LinkedList有什么區(qū)別
6,實現(xiàn)線程的2種方式
7,JVM的內(nèi)存結(jié)構(gòu)
8,Lock與Synchronized的區(qū)別
9,數(shù)據(jù)庫隔離級別有哪些,各自的含義是什么,MYSQL默認的隔離級別是是什么。
10,請解釋如下jvm參數(shù)的含義:
-server -Xms512m -Xmx512m -Xss1024K
-XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=20 XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly。

1、什么是線程安全?

從java的角度看,當(dāng)多個線程同時訪問某個類時,不管運行時環(huán)境采用何種調(diào)用方式或者這些線程將如何交替執(zhí)行,主調(diào)代碼中不需要任何額外的同步或協(xié)同,這個類都能表現(xiàn)出正確的行為,那么就稱這個類是線程安全的。

2、都說String是不可變的,為什么我可以這樣做呢? String a = "1";a = "2";

String不可變指的是String類和其內(nèi)部的char型數(shù)組用final修飾的,這個兩個效果,一、String類用final修飾,不能被繼承;二、char型數(shù)組用final修飾,其指針不得修改指向的堆地址,但是在堆內(nèi)部的數(shù)組里面時可以變換數(shù)值的。String a=”1”;表示初始化一個String賦值為”1”,而s只是一個String對象的引用,指向了剛才這個對象。而 a=”2”;不是在原內(nèi)存地址上修改數(shù)據(jù),而是重新創(chuàng)建了一個新的對象,并將該引用指向這個新的對象;而原來的對象還在常量池中,沒有消失。

3、HashMap的實現(xiàn)原理

HashMap是基于哈希表的Map接口的非同步(線程不安全)實現(xiàn)。并允許使用null值和null鍵。下面以JAVA8為例。

一、HashMap數(shù)據(jù)結(jié)構(gòu)

HashMap的數(shù)據(jù)結(jié)構(gòu)實際上是一個“鏈表散列”的數(shù)據(jù)結(jié)構(gòu),即數(shù)組和鏈表的結(jié)合體(JDK1.8增加了紅黑樹部分)。HashMap的底層是一個數(shù)組結(jié)構(gòu),數(shù)組中的每一項是一個鏈表。當(dāng)新建一個HashMap時,會初始化一個Node類型的數(shù)組,Node[] table,即哈希桶數(shù)組??蛇x參數(shù)為initialCapacity,數(shù)組大小,loadFactor,數(shù)組被填滿程度的最大比例。當(dāng)數(shù)組中的Node的個數(shù)(而不是已占用的位置數(shù))大于initialCapacity*loadFactor時就需要擴容,調(diào)整數(shù)組的大小為當(dāng)前的2倍。同時,初始化容量的大小也是2的次冪(大于等于設(shè)定容量的最小次冪),則數(shù)組的大小在擴容前后都將是2的次冪。默認的capacity為16,loadFactor為0.75。

Node是一個static class,其中包含了key和value,也就是鍵值對,還包含了一個next的Node指針,它持有一個指向下一個元素的引用,這就構(gòu)成了鏈表。負載因子和Hash算法設(shè)計的再合理,也免不了會出現(xiàn)拉鏈過長的情況,一旦出現(xiàn)拉鏈過長,則會嚴重影響HashMap的性能。于是,在JDK1.8版本中,對數(shù)據(jù)結(jié)構(gòu)做了進一步的優(yōu)化,引入了紅黑樹。而當(dāng)鏈表長度太長(默認超過8)時,鏈表就轉(zhuǎn)換為紅黑樹,利用紅黑樹快速增刪改查的特點提高HashMap的性能。

二、HashMap核心方法(源代碼太長不放了)

  • 1、hash方法

設(shè)計者不假定用戶實現(xiàn)了良好的hashCode方法,所以需要對hashCode再計算一次,即在get方法和pu方法計算下標(biāo)時,先對hashCode進行hash操作,然后再通過hash值進一步hash計算得到數(shù)組下標(biāo)。

  • 2、put()方法

當(dāng)put的時候,如果key存在了,那么新的value會代替舊的value,并且如果key存在的情況下,該方法返回的是舊的value,如果key不存在,那么返回null。當(dāng)我們往HashMap中put元素的時候,先根據(jù)key的hashCode重新計算hash值,根據(jù)hash值得到這個元素在數(shù)組中的位置(即下標(biāo)),如果數(shù)組該位置上已經(jīng)存放有其他元素了,那么在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,這樣插入新entry時不需要遍歷鏈表,時間復(fù)雜度為O(1)。如果數(shù)組該位置上沒有元素,就直接將該元素放到此數(shù)組中的該位置上。Java8中對鏈表長度增加了一個閾值,超過閾值鏈表將轉(zhuǎn)化為紅黑樹,查詢時間復(fù)雜度降為O(logn),提高了鏈表過長時的性能。

  • 3、get()方法

根據(jù)key的hash方法得到數(shù)據(jù)下標(biāo),然后遍歷Node對象鏈表,直到找到元素為止。

  • 4.、resize()方法

這里就是使用一個容量更大的數(shù)組(原來的兩倍)來代替已有的容量小的數(shù)組,然后rehash形成新的數(shù)組。因為使用的的是2次冪的擴展(指長度擴為原來2倍),所以,元素的位置要么是在原位置,要么是在原位置再移動2次冪的位置。因此,在擴充HashMap的時候,不需要像JDK1.7的實現(xiàn)那樣重新計算hash,只需要看看原來的hash值新增的那個bit是1還是0,是0的話索引沒變,是1的話索引變成“原索引+oldCap”。

4、三種單例模式,線程安全

5、ArrayList和LinkedList有什么區(qū)別?

ArrayList和LinkedList都是實現(xiàn)了List接口的類,他們都是元素的容器,用于存放對象的引用。但是他們有區(qū)別的。

ArrayList:內(nèi)部使用數(shù)組的形式實現(xiàn)了存儲,實現(xiàn)了RandomAccess接口,利用數(shù)組的下面進行元素的訪問,因此對元素的隨機訪問速度非???。因為是數(shù)組,所以ArrayList在初始化的時候,有初始大小10,插入新元素的時候,會判斷是否需要擴容,擴容的步長是0.5倍原容量,擴容方式是利用數(shù)組的復(fù)制,因此有一定的開銷;

另外,ArrayList在進行元素插入的時候,需要移動插入位置之后的所有元素,位置越靠前,需要位移的元素越多,開銷越大,相反,插入位置越靠后的話,開銷就越小了,如果在最后面進行插入,那就不需要進行位移;

LinkedList:內(nèi)部使用雙向鏈表的結(jié)構(gòu)實現(xiàn)存儲,LinkedList有一個內(nèi)部類作為存放元素的單元,里面有三個屬性,用來存放元素本身以及前后2個單元的引用,另外LinkedList內(nèi)部還有一個header屬性,用來標(biāo)識起始位置,LinkedList的第一個單元和最后一個單元都會指向header,因此形成了一個雙向的鏈表結(jié)構(gòu)。

LinkedList是采用雙向鏈表實現(xiàn)的。所以它也具有鏈表的特點,每一個元素(結(jié)點)的地址不連續(xù),通過引用找到當(dāng)前結(jié)點的上一個結(jié)點和下一個結(jié)點,即插入和刪除效率較高,只需要常數(shù)時間,而get和set則較為低效。

LinkedList的方法和使用和ArrayList大致相同,由于LinkedList是鏈表實現(xiàn)的,所以額外提供了在頭部和尾部添加/刪除元素的方法,也沒有ArrayList擴容的問題。另外,ArrayList和LinkedList都可以實現(xiàn)棧、隊列等數(shù)據(jù)結(jié)構(gòu),但LinkedList本身實現(xiàn)了隊列的接口,所以更推薦用LinkedList來實現(xiàn)隊列和棧。

6、實現(xiàn)線程的2種方式

1、繼承 Thread 類

繼承Thread類,重寫run方法,在啟動線程的時候,調(diào)用了Thread類的start方法。

2、實現(xiàn) Runnable 接口

實現(xiàn)Runnable接口,重寫run方法,實現(xiàn)Runnable接口的實現(xiàn)類的實例對象作為Thread構(gòu)造函數(shù)的target。

  • 1、定義一個類實現(xiàn)Runnable接口,作為線程任務(wù)類。

  • 2、重寫run方法,并實現(xiàn)方法體,方法體的代碼就是線程所執(zhí)行的代碼。

  • 3、定義一個可以運行的類,并在main方法中創(chuàng)建線程任務(wù)類。

  • 4、創(chuàng)建Thread類,并將線程任務(wù)類做為Thread類的構(gòu)造方法傳入。

  • 5、啟動線程。

這樣可以做到線程任務(wù)和線程的控制分離,即解耦。

3、通過線程池創(chuàng)建線程、Executor框架

有返回值,通過Callable接口,實現(xiàn)call方法。執(zhí)行Callable任務(wù),獲取一個Future的對象,在該對象上調(diào)用get就可以獲取到Callable任務(wù)返回的Object對象。

7、JVM的內(nèi)存結(jié)構(gòu)

JVM內(nèi)存結(jié)構(gòu)主要有5大塊。方法區(qū)和堆是所有線程共享的內(nèi)存區(qū)域;而Java棧、本地方法棧和程序計數(shù)器是運行是線程私有的內(nèi)存區(qū)域。

1、Java堆(Heap)

Java堆(Java Heap)是Java虛擬機所管理的內(nèi)存中最大的一塊。Java堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機啟動時創(chuàng)建。此內(nèi)存區(qū)域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這里分配內(nèi)存。

Java堆是垃圾收集器管理的主要區(qū)域,也被稱做“GC堆”。如果從內(nèi)存回收的角度看,現(xiàn)在收集器基本都是采用分代收集算法,所以Java堆中還可以細分為:新生代和老年代;再細致一點的有Eden空間、From Survivor空間、To Survivor空間等。如果在堆中沒有內(nèi)存完成實例分配,并且堆也無法再擴展時,將會拋出OutOfMemoryError異常。

2、方法區(qū)(Method Area)

方法區(qū)(Method Area)與Java堆一樣,是各個線程共享的內(nèi)存區(qū)域,它用于存儲已被虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。

Java虛擬機規(guī)范對這個區(qū)域的限制非常寬松,除了和Java堆一樣不需要連續(xù)的內(nèi)存和可以選擇固定大小或者可擴展外,還可以選擇不實現(xiàn)垃圾收集。相對而言,垃圾收集行為在這個區(qū)域是比較少出現(xiàn)的,但并非數(shù)據(jù)進入了方法區(qū)就如永久代的名字一樣“永久”存在了。這個區(qū)域的內(nèi)存回收目標(biāo)主要是針對常量池的回收和對類型的卸載,一般來說這個區(qū)域的回收“成績”比較難以令人滿意,尤其是類型的卸載,條件相當(dāng)苛刻。運行時常量池是方法區(qū)的一部分。

根據(jù)Java虛擬機規(guī)范的規(guī)定,當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時,將拋出OutOfMemoryError異常。

3、程序計數(shù)器(Program Counter Register)

程序計數(shù)器(Program Counter Register)是一塊較小的內(nèi)存空間,它的作用可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器。

由于Java虛擬機的多線程是通過線程輪流切換并分配處理器執(zhí)行時間的方式來實現(xiàn)的,在任何一個確定的時刻,一個處理器只會執(zhí)行一條線程中的指令。因此,為了線程切換后能恢復(fù)到正確的執(zhí)行位置,每條線程都需要有一個獨立的程序計數(shù)器,各條線程之間的計數(shù)器互不影響,獨立存儲,我們稱這類內(nèi)存區(qū)域為“線程私有”的內(nèi)存。如果線程正在執(zhí)行的是一個Java方法,這個計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令的地址;如果正在執(zhí)行的是Natvie方法,這個計數(shù)器值則為空(Undefined)。此內(nèi)存區(qū)域是唯一一個在Java虛擬機規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。

4、JVM棧(JVM Stacks)

與程序計數(shù)器一樣,Java虛擬機棧(Java Virtual Machine Stacks)也是線程私有的,它的生命周期與線程相同。虛擬機棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法被執(zhí)行的時候都會同時創(chuàng)建一個棧幀(Stack Frame)用于存儲局部變量表、操作棧、動態(tài)鏈接、方法出口等信息。每一個方法被調(diào)用直至執(zhí)行完成的過程,就對應(yīng)著一個棧幀在虛擬機棧中從入棧到出棧的過程。

局部變量表存放了編譯期可知的各種基本數(shù)據(jù)類型、對象引用和returnAddress類型。其中64位長度的long和double類型的數(shù)據(jù)會占用2個局部變量空間(Slot),其余的數(shù)據(jù)類型只占用1個。局部變量表所需的內(nèi)存空間在編譯期間完成分配,當(dāng)進入一個方法時,這個方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。

在Java虛擬機規(guī)范中,對這個區(qū)域規(guī)定了兩種異常狀況:如果線程請求的棧深度大于虛擬機所允許的深度,將拋出StackOverflowError異常;如果虛擬機棧無法申請到足夠的內(nèi)存時會拋出OutOfMemoryError異常。

5、本地方法棧(Native Method Stacks)

本地方法棧(Native Method Stacks)與虛擬機棧非常相似的,其區(qū)別是虛擬機棧為虛擬機執(zhí)行Java方法服務(wù),而本地方法棧則是為虛擬機使用到的Native方法服務(wù)。虛擬機規(guī)范中對本地方法棧中的方法使用的語言、使用方式與數(shù)據(jù)結(jié)構(gòu)并沒有強制規(guī)定,具體的虛擬機可以自由實現(xiàn)它。HotSpot直接就把本地方法棧和虛擬機棧合二為一。與虛擬機棧一樣,本地方法棧區(qū)域也會拋出StackOverflowError和OutOfMemoryError異常。

8、Lock與Synchronized的區(qū)別

下面說明Lock和Synchronized的區(qū)別:

  • 1、synchronized是Java的關(guān)鍵字,可以用來修飾一個方法或者一個代碼塊,而Lock是一個接口,主要實現(xiàn)是ReentrantLock類。

  • 2、synchronized就不是可中斷鎖,而Lock是可中斷鎖。 如果某一線程A正在執(zhí)行鎖中的代碼,另一線程B正在等待獲取該鎖,由于等待時間過長,線程B不想等待,想先處理其他事情,它可以中斷自己或者在別的線程中中斷它。

  • 3、synchronized是非公平鎖,它無法保證等待的線程獲取鎖的順序。ReentrantLock,默認情況下是非公平鎖,但是可以設(shè)置為公平鎖。

  • 4、synchronized在發(fā)生異常時,會自動釋放線程占有的鎖,因此不會導(dǎo)致死鎖現(xiàn)象發(fā)生;而Lock在發(fā)生異常時,如果沒有主動通過unLock()去釋放鎖,則很可能造成死鎖現(xiàn)象,因此使用Lock時需要在finally塊中釋放鎖;

  • 5、通過Lock可以知道有沒有成功獲取鎖,而synchronized不行。

9、數(shù)據(jù)庫隔離級別有哪些,各自的含義是什么,MYSQL默認的隔離級別是是什么。

數(shù)據(jù)庫的事務(wù)隔離級別有四種,分別是,未提交讀,提交讀,可重復(fù)讀,串行讀。

  • 1、未提交讀(Read Uncommitted):允許臟讀,可能讀取到其他會話中未提交事務(wù)修改的數(shù)據(jù)

  • 2、提交讀(Read Committed):一個事務(wù)只能看見其他已經(jīng)提交事務(wù)的數(shù)據(jù)修改。這會導(dǎo)致不可重復(fù)讀,即在一個事務(wù)中,多次讀同一數(shù)據(jù)。在這個事務(wù)還沒有結(jié)束時,另外一個事務(wù)訪問修改同一數(shù)據(jù)。在第一個事務(wù)中的兩次讀數(shù)據(jù)之間,由于第二個事務(wù)的修改,第一個事務(wù)兩次讀到的的數(shù)據(jù)可能是不一樣的。

  • 3、可重復(fù)讀(Repeated Read): 在同一個事務(wù)內(nèi)的查詢都是事務(wù)開始時刻一致的。它確保同一事務(wù)的多個實例在并發(fā)讀取數(shù)據(jù)時,會看到同樣的數(shù)據(jù)行。但是可能會有幻讀,即當(dāng)一個事務(wù)讀取某一范圍的數(shù)據(jù)行時,另一個事務(wù)又在該范圍內(nèi)插入了新行,當(dāng)該事務(wù)再讀取該范圍的數(shù)據(jù)行時,會發(fā)現(xiàn)有新的“幻影” 行 。

  • 4、串行讀(Serializable) :完全串行化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞,從而解決幻讀問題。

MYSQL的InnoDB引擎默認是可重復(fù)讀

10、請解釋如下jvm參數(shù)的含義:

-server -Xms512m -Xmx512m -Xss1024K

-XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=20 XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly。

-servier 設(shè)置jvm為服務(wù)器模式,

-Xms512m -Xmx512m -Xss1024K,JAVA堆最小為512M,最大為512M,JVM棧為1024K

-XX:PermSize=256m -XX:MaxPermSize=512m JVM初始分配方法區(qū)(非堆)內(nèi)存為256M,最大為512M(在HotSpot虛擬機為持久代,該參數(shù)在java8以后失效)

-XX:MaxTenuringThreshold=20對象在新生代堅持20次Mirror GC后,晉升到老年代。

XX:CMSInitiatingOccupancyFraction=80 設(shè)置CMS收集器在老年代空間被占用80%后觸發(fā)GC。

-XX:+UseCMSInitiatingOccupancyOnly。關(guān)閉CMS收集器的動態(tài)檢測機制。禁止CMS根據(jù)自己的預(yù)測自動執(zhí)行垃圾回收。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在一個方法內(nèi)部定義的變量都存儲在棧中,當(dāng)這個函數(shù)運行結(jié)束后,其對應(yīng)的棧就會被回收,此時,在其方法體中定義的變量將不...
    Y了個J閱讀 4,575評論 1 14
  • 從三月份找實習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍閱讀 42,814評論 11 349
  • Java SE 基礎(chǔ): 封裝、繼承、多態(tài) 封裝: 概念:就是把對象的屬性和操作(或服務(wù))結(jié)合為一個獨立的整體,并盡...
    Jayden_Cao閱讀 2,247評論 0 8
  • 1、字符串swif中字符串的類型是String ,用""修飾1-1、字符串的定義: 定義可變字符串: var...
    7dfa9c18c1d1閱讀 151評論 0 0
  • 上篇文章我總結(jié)了自己碎片化學(xué)習(xí)了近一年,卻依然缺失方向、沒有明確目標(biāo)。是因自己沒深入思考過學(xué)到的知識是否能構(gòu)...
    詩苑的成長花園閱讀 303評論 1 0

友情鏈接更多精彩內(nèi)容