面試整理-Java綜合高級篇(吐血整理)

Java面試總結

  • 交流或更多內容請關注個人公衆號:nezha_blog
  • 個人技術博客:nezha.github.io

微信公衆號

1.你用過哪些集合類?

大公司最喜歡問的Java集合類面試題 40個Java集合面試問題和答案 java.util.Collections 是一個包裝類。它包含有各類有關集合操做的靜態多態方法。 java.util.Collection 是一個集合接口。它提供了對集合對象進行基本操做的通用接口方法。html

Collection ├List │├LinkedList │├ArrayList │└Vector │ └Stack └Set Map ├Hashtable ├HashMap └WeakHashMapjava


ArrayList、HashMap、TreeMap和HashTable類提供對元素的隨機訪問。git

線程安全github

Vector HashTable(不容許插空值)面試

非線程安全算法

ArrayList LinkedList HashMap(容許插入空值) HashSet TreeSet TreeMap(基於紅黑樹的Map實現)sql

2.你說說 arraylist 和 linkedlist 的區別?

ArrayList和LinkedList二者都實現了List接口,可是它們之間有些不一樣。 (1)ArrayList是由Array所支持的基於一個索引的數據結構,因此它提供對元素的隨機訪問 (2)與ArrayList相比,在LinkedList中插入、添加和刪除一個元素會更快 (3)LinkedList比ArrayList消耗更多的內存,由於LinkedList中的每一個節點存儲了先後節點的引用數據庫

3.HashMap 底層是怎麼實現的?還有什麼處理哈希衝突的方法?

處理哈希衝突的方法:編程

解決HashMap通常沒有什麼特別好的方式,要不擴容從新hash要不優化衝突的鏈表結構數組

1.開放定地址法-線性探測法 2.開放定地址法-平方探查法 3.鏈表解決-能夠用紅黑樹提升查找效率

image.png

HashMap簡介 HashMap 是一個散列表,它存儲的內容是鍵值對(key-value)映射。 HashMap 繼承於AbstractMap,實現了Map、Cloneable、java.io.Serializable接口。 HashMap 的實現不是同步的,這意味着它不是線程安全的,但能夠用 Collections的synchronizedMap方法使HashMap具備線程安全的能力。它的key、value均可覺得null。此外,HashMap中的映射不是有序的。 HashMap 的實例有兩個參數影響其性能:「初始容量」 和 「加載因子」。初始容量默認是16。默認加載因子是 0.75, 這是在時間和空間成本上尋求一種折衷。加載因子太高雖然減小了空間開銷,但同時也增長了查詢成本. HashMap是數組+鏈表+紅黑樹(JDK1.8增長了紅黑樹部分)實現的,當鏈表長度太長(默認超過8)時,鏈表就轉換爲紅黑樹.

image.png

Java8系列之從新認識HashMap 功能實現-方法

  1. 肯定哈希桶數組索引位置 :這裏的Hash算法本質上就是三步:取key的hashCode值、高位運算、取模運算。
方法一:
static final int hash(Object key) {   //jdk1.8 & jdk1.7
     int h;
     // h = key.hashCode() 爲第一步 取hashCode值
     // h ^ (h >>> 16) 爲第二步 高位參與運算
     return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
方法二:
static int indexFor(int h, int length) {  //jdk1.7的源碼,jdk1.8沒有這個方法,可是實現原理同樣的
     return h & (length-1);  //第三步 取模運算
}
複製代碼
  1. 分析HashMap的put方法
    image.png
  1. 擴容機制:原來的兩倍

4.熟悉什麼算法,還有說說他們的時間複雜度?

經典排序算法總結與實現

image.png

5.ArrayList和Vector的底層代碼和他們的增加策略,它們是如何進行擴容的?

ArrayList 默認數組大小是10,其中ensureCapacity擴容,trimToSize容量調整到適中,擴展後數組大小爲((原數組長度*1.5)與傳遞參數中較大者. Vector的擴容,是能夠指定擴容因子,同時Vector擴容策略是:1.原來容量的2倍,2.原來容量+擴容參數值。

詳細內容能夠配合閱讀源碼

6.jvm 原理。程序運行區域劃分

問:Java運行時數據區域? 回答:包括程序計數器、JVM棧、本地方法棧、方法區、堆 問:方法區裏存放什麼? 本地方法棧:和jvm棧所發揮的做用相似,區別是jvm棧爲jvm執行java方法(字節碼)服務,而本地方法棧爲jvm使用的native方法服務。 JVM棧:局部變量表、操做數棧、動態連接、方法出口。 方法區:用於存儲已被虛擬機加載的類信息,常量、靜態變量、即時編譯器編譯後的代碼等。 堆:存放對象實例。

7.minor GC 與 Full GC,分別何時會觸發? 。分別採用哪一種垃圾回收算法?簡單介紹算法

GC(或Minor GC):收集 生命週期短的區域(Young area)。 Full GC (或Major GC):收集生命週期短的區域(Young area)和生命週期比較長的區域(Old area)對整個堆進行垃圾收集。 新生代一般存活時間較短基於Copying算法進行回收,將可用內存分爲大小相等的兩塊,每次只使用其中一塊;當這一塊用完了,就將還活着的對象複製到另外一塊上,而後把已使用過的內存清理掉。在HotSpot裏,考慮到大部分對象存活時間很短將內存分爲Eden和兩塊Survivor,默認比例爲8:1:1。代價是存在部份內存空間浪費,適合在新生代使用; 老年代新生代不一樣,老年代對象存活的時間比較長、比較穩定,所以採用標記(Mark)算法來進行回收,所謂標記就是掃描出存活的對象,而後再進行回收未被標記的對象,回收後對用空出的空間要麼進行合併、要麼標記出來便於下次進行分配,總之目的就是要減小內存碎片帶來的效率損耗。 在執行機制上JVM提供了串行GC(Serial MSC)、並行GC(Parallel MSC)和併發GC(CMS)。

Minor GC ,Full GC 觸發條件

  • Minor GC觸發條件:當Eden區滿時,觸發Minor GC。

  • Full GC觸發條件:

  • (1)調用System.gc時,系統建議執行Full GC,可是沒必要然執行

  • (2)老年代空間不足

  • (3)方法去空間不足

  • (4)經過Minor GC後進入老年代的平均大小大於老年代的可用內存

  • (5)由Eden區、From Space區向To Space區複製時,對象大小大於To Space可用內存,則把該對象轉存到老年代,且老年代的可用內存小於該對象大小

8.HashMap 實現原理

在java編程語言中,最基本的結構就是兩種,一個是數組,另一個是模擬指針(引用),全部的數據結構均可以用這兩個基本結構來構造的,HashMap也不例外。HashMap其實是一個「鏈表散列」的數據結構,即數組和鏈表的結合體。

9.java.util.concurrent 包下使用過哪些

1.阻塞隊列 BlockingQueue( ArrayBlockingQueue, DelayQueue, LinkedBlockingQueue, SynchronousQueue,LinkedTransferQueue,LinkedBlockingDeque) 2.ConcurrentHashMap 3.Semaphore--信號量 4.CountDownLatch--閉鎖 5.CyclicBarrier--柵欄 6.Exchanger--交換機 7.Executor->ThreadPoolExecutor,ScheduledThreadPoolExecutor

Semaphore semaphore = new Semaphore(1);  
//critical section 
semaphore.acquire();  
...  
semaphore.release();
複製代碼

8.鎖 Lock--ReentrantLock,ReadWriteLock,Condition,LockSupport

Lock lock = new ReentrantLock();  
lock.lock();  
//critical section 
lock.unlock();
複製代碼

10.concurrentMap 和 HashMap 區別

1.hashMap能夠有null的鍵,concurrentMap不能夠有 2.hashMap是線程不安全的,在多線程的時候須要Collections.synchronizedMap(hashMap),ConcurrentMap使用了重入鎖保證線程安全。 3.在刪除元素時候,二者的算法不同。 ConcurrentHashMapHashtable主要區別就是圍繞着鎖的粒度以及如何鎖,能夠簡單理解成把一個大的HashTable分解成多個,造成了鎖分離。

11.信號量是什麼,怎麼使用?volatile關鍵字是什麼?

信號量-semaphore:荷蘭著名的計算機科學家Dijkstra 於1965年提出的一個同步機制。是在多線程環境下使用的一種設施, 它負責協調各個線程, 以保證它們可以正確、合理的使用公共資源。 整形信號量:表示共享資源狀態,且只能由特殊的原子操做改變整型量。 同步與互斥:同類進程爲互斥關係(打印機問題),不一樣進程爲同步關係(消費者生產者)。


使用volatile關鍵字是解決同步問題的一種有效手段。 java volatile關鍵字預示着這個變量始終是「存儲進入了主存」。更精確的表述就是每一次讀一個volatile變量,都會從主存讀取,而不是CPU的緩存。一樣的道理,每次寫一個volatile變量,都是寫回主存,而不只僅是CPU的緩存。 Java 保證volatile關鍵字保證變量的改變對各個線程是可見的。

image.png

12.阻塞隊列了解嗎?怎麼使用

阻塞隊列 (BlockingQueue)是Java util.concurrent包下重要的數據結構,BlockingQueue提供了線程安全的隊列訪問方式:當阻塞隊列進行插入數據時,若是隊列已滿,線程將會阻塞等待直到隊列非滿;從阻塞隊列取數據時,若是隊列已空,線程將會阻塞等待直到隊列非空。併發包下不少高級同步類的實現都是基於BlockingQueue實現的。

image.png
image.png

以ArrayBlockingQueue爲例,咱們先來看看代碼:

public void put(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length)
            notFull.await();
        enqueue(e);
    } finally {
        lock.unlock();
    }
}
複製代碼

put方法的實現能夠看出,它先獲取了鎖,而且獲取的是可中斷鎖,而後判斷當前元素個數是否等於數組的長度,若是相等,則調用notFull.await()進行等待,當被其餘線程喚醒時,經過enqueue(e)方法插入元素,最後解鎖。

/** * Inserts element at current put position, advances, and signals. * Call only when holding lock. */
private void enqueue(E x) {
    // assert lock.getHoldCount() == 1;
    // assert items[putIndex] == null;
    final Object[] items = this.items;
    items[putIndex] = x;
    if (++putIndex == items.length) putIndex = 0;
    count++;
    notEmpty.signal();
}
複製代碼

插入成功後,經過notEmpty喚醒正在等待取元素的線程。

13.Java中的NIO,BIO,AIO分別是什麼?

IO的方式一般分爲幾種,同步阻塞的BIO、同步非阻塞的NIO、異步非阻塞的AIO

1.BIO,同步阻塞式IO,簡單理解:一個鏈接一個線程.BIO方式適用於鏈接數目比較小且固定的架構,這種方式對服務器資源要求比較高,併發侷限於應用中,JDK1.4之前的惟一選擇,但程序直觀簡單易理解。

在JDK1.4以前,用Java編寫網絡請求,都是創建一個ServerSocket,而後,客戶端創建Socket時就會詢問是否有線程能夠處理,若是沒有,要麼等待,要麼被拒絕。即:一個鏈接,要求Server對應一個處理線程。

2.NIO,同步非阻塞IO,簡單理解:一個請求一個線程.NIO方式適用於鏈接數目多且鏈接比較短(輕操做)的架構,好比聊天服務器,併發侷限於應用中,編程比較複雜,JDK1.4開始支持。

NIO自己是基於事件驅動思想來完成的,其主要想解決的是BIO的大併發問題: 在使用同步I/O的網絡應用中,若是要同時處理多個客戶端請求,或是在客戶端要同時和多個服務器進行通信,就必須使用多線程來處理。也就是說,將每個客戶端請求分配給一個線程來單獨處理。這樣作雖然能夠達到咱們的要求,但同時又會帶來另一個問題。因爲每建立一個線程,就要爲這個線程分配必定的內存空間(也叫工做存儲器),並且操做系統自己也對線程的總數有必定的限制。若是客戶端的請求過多,服務端程序可能會由於不堪重負而拒絕客戶端的請求,甚至服務器可能會所以而癱瘓。

3.AIO,異步非阻塞IO,簡單理解:一個有效請求一個線程.AIO方式使用於鏈接數目多且鏈接比較長(重操做)的架構,好比相冊服務器,充分調用OS參與併發操做,編程比較複雜,JDK7開始支持。

14.類加載機制是怎樣的

JVM中類的裝載是由ClassLoader和它的子類來實現的,Java ClassLoader是一個重要的Java運行時系統組件。它負責在運行時查找和裝入類文件的類。 類加載的五個過程:加載、驗證、準備、解析、初始化。

從類被加載到虛擬機內存中開始,到卸御出內存爲止,它的整個生命週期分爲7個階段,加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸御(Unloading)。其中驗證、準備、解析三個部分統稱爲鏈接。

15.什麼是冪等性

所謂冪等,簡單地說,就是對接口的屢次調用所產生的結果和調用一次是一致的。 那麼咱們爲何須要接口具備冪等性呢?設想一下如下情形:

  • 在App中下訂單的時候,點擊確認以後,沒反應,就又點擊了幾回。在這種狀況下,若是沒法保證該接口的冪等性,那麼將會出現重複下單問題。
  • 在接收消息的時候,消息推送重複。若是處理消息的接口沒法保證冪等,那麼重複消費消息產生的影響可能會很是大。

16.有哪些 JVM 調優經驗

Jvm參數總結:http://linfengying.com/?p=2470

內存參數

參數 做用
-Xmx 堆大小的最大值。當前主流虛擬機的堆都是可擴展的
-Xms 堆大小的最小值。能夠設置成和 -Xmx 同樣的值
-Xmn 新生代的大小。現代虛擬機都是「分代」的,所以堆空間由新生代和老年代組成。新生代增大,相應地老年代就減少。Sun官方推薦新生代佔整個堆的3/8
-Xss 每一個線程的堆棧大小。該值影響一臺機器可以建立的線程數上限
-XX:MaxPermSize= 永久代的最大值。永久代是 HotSpot 特有的,HotSpot 用永久代來實現方法區
-XX:PermSize= 永久代的最小值。能夠設置成和 -XX:MaxPermSize 同樣的值
-XX:SurvivorRatio= Eden 和 Survivor 的比值。基於「複製」的垃圾收集器又會把新生代分爲一個 Eden 和兩個 Survivor,若是該參數爲8,就表示 Eden
-XX:PretenureSizeThreshold= 直接晉升到老年代的對象大小。大於這個參數的對象將直接在老年代分配。默認值爲0,表示不啓用
-XX:HandlePromotionFailure= 是否容許分配擔保失敗。在 JDK 6 Update 24 後該參數已經失效。
-XX:MaxTenuringThreshold= 對象晉升到老年代的年齡。對象每通過一次 Minor GC 後年齡就加1,超過這個值時就進入老年代。默認值爲15
-XX:MaxDirectMemorySize= 直接內存的最大值。對於頻繁使用 nio 的應用,應該顯式設置該參數,默認值爲0

GC參數

垃圾收集器 參數 備註
Serial(新生代) -XX:+UseSerialGC 虛擬機在 Client 模式下的默認值,打開此開關後,使用 Serial + Serial Old 的收集器組合。Serial 是一個單線程的收集器
ParNew(新生代) -XX:+UseParNewGC 強制使用 ParNew,打開此開關後,使用 ParNew + Serial Old 的收集器組合。ParNew 是一個多線程的收集器,也是 server 模式下首選的新生代收集器
-XX:ParallelGCThreads= 垃圾收集的線程數
Parallel Scavenge(新生代) -XX:+UseParallelGC 虛擬機在 Server 模式下的默認值,打開此開關後,使用 Parallel Scavenge + Serial Old 的收集器組合
-XX:MaxGCPauseMillis= 單位毫秒,收集器儘量保證單次內存回收停頓的時間不超過這個值。
-XX:GCTimeRatio= 總的用於 gc 的時間佔應用程序的百分比,該參數用於控制程序的吞吐量
-XX:+UseAdaptiveSizePolicy 設置了這個參數後,就再也不須要指定新生代的大小(-Xmn)、 Eden 和 Survisor 的比例(-XX:SurvivorRatio)以及晉升老年代對象的年齡(-XX:PretenureSizeThreshold)了,由於該收集器會根據當前系統的運行狀況自動調整。固然前提是先設置好前兩個參數。
Serial Old(老年代) Serial Old 是 Serial 的老年代版本,主要用於 Client 模式下的老生代收集,同時也是 CMS 在發生 Concurrent Mode Failure 時的後備方案
Parallel Old(老年代) -XX:+UseParallelOldGC 打開此開關後,使用 Parallel Scavenge + Parallel Old 的收集器組合。Parallel Old 是 Parallel Scavenge 的老年代版本,在注重吞吐量和 CPU 資源敏感的場合,能夠優先考慮這個組合
CMS(老年代) -XX:+UseConcMarkSweepGC 打開此開關後,使用 ParNew + CMS 的收集器組合。
-XX:CMSInitiatingOccupancyFraction= CMS 收集器在老年代空間被使用多少後觸發垃圾收集
-XX:+UseCMSCompactAtFullCollection 在完成垃圾收集後是否要進行一次內存碎片整理
-XX:CMSFullGCsBeforeCompaction= 在進行若干次垃圾收集後才進行一次內存碎片整理

附圖:能夠配合使用的收集器組合

image.png

上面有7中收集器,分爲兩塊,上面爲新生代收集器,下面是老年代收集器。若是兩個收集器之間存在連線,就說明它們能夠搭配使用。

其餘參數

參數 做用
-verbose:class 打印類加載過程
-XX:+PrintGCDetails 發生垃圾收集時打印 gc 日誌,該參數會自動帶上 -verbose:gc 和 -XX:+PrintGC
-XX:+PrintGCDateStamps / -XX:+PrintGCTimeStamps 打印 gc 的觸發事件,能夠和 -XX:+PrintGC 和 -XX:+PrintGCDetails 混用
-Xloggc: gc 日誌路徑
-XX:+HeapDumpOnOutOfMemoryError 出現 OOM 時 dump 出內存快照用於過後分析
-XX:HeapDumpPath= 堆轉儲快照的文件路徑

17.分佈式 CAP 瞭解嗎?

一致性(Consistency) 可用性(Availability) 分區容忍性(Partition tolerance)

18.Java中HashMap的key值要是爲類對象則該類須要知足什麼條件?

須要同時重寫該類的hashCode()方法和它的equals()方法。

當程序試圖將一個 key-value 對放入 HashMap 中時,程序首先根據該 key 的 hashCode() 返回值決定該 Entry 的存儲位置:若是兩個 Entry 的 key 的 hashCode() 返回值相同,那它們的存儲位置相同。若是這兩個 Entry 的 key 經過 equals 比較返回 true,新添加 Entry 的 value 將覆蓋集合中原有 Entry 的 value,但 key 不會覆蓋。若是這兩個 Entry 的 key 經過 equals 比較返回 false,新添加的 Entry 將與集合中原有 Entry 造成 Entry 鏈,並且新添加的 Entry 位於 Entry 鏈的頭部——具體說明繼續看 addEntry() 方法的說明。

19.java 垃圾回收會出現不可回收的對象嗎?怎麼解決內存泄露問題?怎麼定位問題源?

通常不會有不可回收的對象,由於如今的GC會回收不可達內存。

20.終止線程有幾種方式?終止線程標記變量爲何是 valotile 類型?

1.線程正常執行完畢,正常結束 2.監視某些條件,結束線程的不間斷運行 3.使用interrupt方法終止線程

在定義exit時,使用了一個Java關鍵字volatile,這個關鍵字的目的是使exit同步,也就是說在同一時刻只能由一個線程來修改exit的值

21.用過哪些併發的數據結構? cyclicBarrier 什麼功能?信號量做用?數據庫讀寫阻塞怎麼解決

  • 主要有鎖機制,而後基於CAS的concurrent包。
  • CyclicBarrier的字面意思是可循環使用(Cyclic)的屏障(Barrier)。它要作的事情是,讓一組線程到達一個屏障(也能夠叫同步點)時被阻塞,直到最後一個線程到達屏障時,屏障纔會開門,全部被屏障攔截的線程纔會繼續幹活。CyclicBarrier默認的構造方法是CyclicBarrier(int parties),其參數表示屏障攔截的線程數量,每一個線程調用await方法告訴CyclicBarrier我已經到達了屏障,而後當前線程被阻塞。 CountDownLatch的計數器只能使用一次。而CyclicBarrier的計數器可使用reset() 方法重置。
  • **Semaphore(信號量)是用來控制同時訪問特定資源的線程數量,它經過協調各個線程,以保證合理的使用公共資源。**不少年以來,我都以爲從字面上很難理解Semaphore所表達的含義,只能把它比做是控制流量的紅綠燈,好比XX馬路要限制流量,只容許同時有一百輛車在這條路上行使,其餘的都必須在路口等待,因此前一百輛車會看到綠燈,能夠開進這條馬路,後面的車會看到紅燈,不能駛入XX馬路,可是若是前一百輛中有五輛車已經離開了XX馬路,那麼後面就容許有5輛車駛入馬路,這個例子裏說的車就是線程,駛入馬路就表示線程在執行,離開馬路就表示線程執行完成,看見紅燈就表示線程被阻塞,不能執行。

22.關於抽象類和接口的關係

簡言之抽象類是一種功能不全的類,接口只是一個抽象方法聲明和靜態不能被修改的數據的集合,二者都不能被實例化。 從某種意義上說,接口是一種特殊形式的抽象類,在java語言中抽象類表示的是一種繼承關係,一個類只能繼承繼承一個抽象類,而一個類卻能夠實現多個接口。在許多狀況下,接口確實能夠代替抽象類,若是你不須要刻意表達屬性上的繼承的話。

23.堆內存和棧內存的區別

寄存器:JVM內部虛擬寄存器,存取速度很是快,程序不可控制。 :保存局部變量的值包括:1.保存基本數據類型的值;2.保存引用變量,即堆區對象的引用(指針)。也能夠用來保存加載方法時的幀。 :用來存放動態產生的數據,好比new出來的對象。注意建立出來的對象只包含屬於各自的成員變量,並不包括成員方法。由於同一個類的對象擁有各自的成員變量,存儲在各自的堆中,可是他們共享該類的方法,並非每建立一個對象就把成員方法複製一次。 常量池:JVM爲每一個已加載的類型維護一個常量池,常量池就是這個類型用到的常量的一個有序集合。包括直接常量(基本類型,String)和對其餘類型、方法、字段的符號引用(1)。池中的數據和數組同樣經過索引訪問。因爲常量池包含了一個類型全部的對其餘類型、方法、字段的符號引用,因此常量池在Java的動態連接中起了核心做用。常量池存在於堆中。 代碼段:用來存放從硬盤上讀取的源程序代碼。 數據段:用來存放static修飾的靜態成員(在java中static的做用就是說明該變量,方法,代碼塊是屬於類的仍是屬於實例的)。

image.png

24.關於Java文件的內部類的解釋?匿名內部類是什麼?如何訪問在其外面定義的變量?

java中的內部類總結 靜態內部類不能訪問外部類非靜態的成員 ###25.關於重載和重寫的區別 重載是overload,是一個類中同方法名的不一樣具體實現。而後重寫是override,是子類重寫父類中的方法。

26.String、StringBuffer與StringBuilder之間區別

1.三者在執行速度方面的比較:StringBuilder > StringBuffer > String

String:字符串常量 StringBuffer:字符串變量 StringBuilder:字符串變量

2.StringBuilder:線程非安全的,StringBuffer:線程安全的 **對於三者使用的總結: ** 1.若是要操做少許的數據用 = String 2.單線程操做字符串緩衝區 下操做大量數據 = StringBuilder 3.多線程操做字符串緩衝區 下操做大量數據 = StringBuffer

27.運行時異常與通常異常有何異同?常見異常

Java提供了兩類主要的異常:runtime exception和checked exception 常見異常:NullPointerException、IndexOutOfBoundsException、ClassNotFoundException,IllegalArgumentException,ClassCastException(數據類型轉換異常) ###28.error和exception有什麼區別? error 表示恢復不是不可能但很困難的狀況下的一種嚴重問題。好比說內存溢出。不可能期望程序能處理這樣的狀況。 exception表示一種設計或實現問題。也就是說,它表示若是程序運行正常,從不會發生的狀況。 ###29.Java異常處理機制

image.png

1.捕獲異常:try、catch 和 finally 2.拋出異常 2.1. throws拋出異常

methodname throws Exception1,Exception2,..,ExceptionN  
{  }  
複製代碼

30.java中有幾種方法能夠實現一個線程?

Java多線程學習(吐血超詳細總結) 40個Java多線程問題總結


1.class Thread1 extends Thread{},而後重寫run方法 2.class Thread2 implements Runnable{},而後重寫run方法 3.class Thread3 implements Callable{},而後new FutureTask(thread3),再用new Thread(future)封裝。

class Thread1 extends Thread {
    private String name;
    public Thread1(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + "運行--->>>" + i);
        }
    }
    
    public static void main(String[] args) {
        Thread1 mTh11=new Thread1("A");
        Thread1 mTh12=new Thread1("B");
        mTh1.start();
        mTh2.start();
    }
}


class Thread2 implements Runnable {
    private String name;
    private int count = 15;
    public Thread2() {
    }
    public Thread2(String name) {
        this.name = name;
    }
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "運行 : " + count--);
        }
    }
    public static void main(String[] args) {
        Thread2 mTh2 = new Thread2();
        new Thread(mTh2, "C").start();
        new Thread(mTh2, "D").start();
    }
}


class MyCallableThread implements Callable<Integer>{
        public Integer call() throws Exception {
        int i = 0;
        for(;i<100;i++)
        {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
        return i;
    }
    
    public static void main(String[] args) {
        MyCallableThread mct = new MyCallableThread();
        FutureTask<Integer> ft = new FutureTask<Integer>(mct);
        for(int i = 0;i < 100;i++)
        {
            System.out.println(Thread.currentThread().getName()+" 的循環變量i的值"+i);
            if(i==20)
            {
                new Thread(ft,"有返回值的線程").start();
            }
        }
        try
        {
            System.out.println("子線程的返回值:"+ft.get());
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        } catch (ExecutionException e)
        {
            e.printStackTrace();
        }
    }
}

複製代碼

若是一個類繼承Thread,則不適合資源共享。可是若是實現了Runable接口的話,則很容易的實現資源共享。

31.Java中經常使用的類,包,接口。

class: 'Date','System','Calender','Math','ArrayList','HashMap' package: 'java.lang','java.util','java.io','java.sql','java.net' interface: 'Collection','Map','List','Runnable','Callable'

32.java在處理線程同步時,經常使用方法有:

一、synchronized關鍵字。 二、Lock顯示加鎖。 三、信號量Semaphore。 四、CAS算法 五、concurrent包

33.Spring IOC/AOP?

回答了IOC/DI、AOP的概念。 AOP(Aspect-OrientedProgramming,面向方面編程),能夠說是OOP(Object-Oriented Programing,面向對象編程)的補充和完善。 OOP引入封裝、繼承和多態性等概念來創建一種對象層次結構,用以模擬公共行爲的一個集合。當咱們須要爲分散的對象引入公共行爲的時候,OOP則顯得無能爲力。 也就是說,OOP容許你定義從上到下的關係,但並不適合定義從左到右的關係。例如日誌功能。日誌代碼每每水平地散佈在全部對象層次中,而與它所散佈到的對象的核心功能毫無關係。 對於其餘類型的代碼,如安全性、異常處理和透明的持續性也是如此。這種散佈在各處的無關的代碼被稱爲橫切(cross-cutting)代碼, 在OOP設計中,它致使了大量代碼的重複,而不利於各個模塊的重用。 依賴注入(Dependency Injection)和控制反轉(Inversion of Control)是同一個概念。 當某個角色(多是一個Java實例,調用者)須要另外一個角色(另外一個Java實例,被調用者)的協助時,在傳統的程序設計過程當中,一般由調用者來建立被調用者的實例。 但在Spring裏,建立被調用者的工做再也不由調用者來完成,所以稱爲控制反轉;建立被調用者 實例的工做一般由Spring容器來完成,而後注入調用者,所以也稱爲依賴注入。 無論是依賴注入,仍是控制反轉,都說明Spring採用動態、靈活的方式來管理各類對象。對象與對象之間的具體實現互相透明。 在理解依賴注入以前,看以下這個問題在各類社會形態裏如何解決:一我的(Java實例,調用者)須要一把斧子(Java實例,被調用者)。

34.對JVM的垃圾回收的認識?

垃圾回收器的做用是查找和回收(清理)無用的對象。以便讓JVM更有效的使用內存。

35.進程與線程的區別,及其通訊方式

線程與進程的區別及其通訊方式 區別 1.一個程序至少有一個進程,一個進程至少有一個線程. 2.進程在執行過程當中擁有獨立的內存單元,而多個線程共享內存 3.線程是進程的一個實體,是CPU調度和分派的基本單位

  • 進程間通訊
1.管道(Pipe)及有名管道(named pipe)
2.信號(Signal)
3.消息隊列(Message)
4.共享內存
5.信號量(semaphore)
6.套接口(Socket)
複製代碼

36.JVM如何GC,新生代,老年代,持久代,都存儲哪些東西?

JVM的GC算法有:引用計數器算法根搜索方法

新生成的對象首先都是放在年輕代的。年輕代的目標就是儘量快速的收集掉那些生命週期短的對象。

在年輕代中經歷了N次垃圾回收後仍然存活的對象,就會被放到年老代中。所以,能夠認爲年老代中存放的都是一些生命週期較長的對象。

持久代主要存放的是Java類的類信息

37.JVM分爲哪些區,每個區幹嗎的?

問:Java運行時數據區域? 回答:包括程序計數器、JVM棧、本地方法棧、方法區、堆 問:方法區裏存放什麼? 本地方法棧:和jvm棧所發揮的做用相似,區別是jvm棧爲jvm執行java方法(字節碼)服務,而本地方法棧爲jvm使用的native方法服務。 JVM棧:局部變量表、操做數棧、動態連接、方法出口。 方法區:用於存儲已被虛擬機加載的類信息,常量、靜態變量、即時編譯器編譯後的代碼等。 堆:存放對象實例。

38.GC用的引用可達性分析算法中,哪些對象可做爲GC Roots對象?

  • 虛擬機棧(棧幀中的本地變量表)中引用的對象;
  • 方法區中類靜態屬性引用的對象;
  • 方法區中常量引用的對象;
  • 本地方法棧中JNI(即通常說的Native方法)引用的對象

39.用什麼工具調試程序?jmap、jstack,JConsole,用過嗎?

虛擬機性能監控與調優實戰--博客

40.線程池用過嗎?

線程池--併發編程網 - ifeve.com

線程池(Thread Pool)對於限制應用程序中同一時刻運行的線程數頗有用。由於每啓動一個新線程都會有相應的性能開銷,每一個線程都須要給棧分配一些內存等等。

咱們能夠把併發執行的任務傳遞給一個線程池,來替代爲每一個併發執行的任務都啓動一個新的線程。只要池裏有空閒的線程,任務就會分配給一個線程執行。在線程池的內部,任務被插入一個阻塞隊列(Blocking Queue ),線程池裏的線程會去取這個隊列裏的任務。當一個新任務插入隊列時,一個空閒線程就會成功的從隊列中取出任務而且執行它。

41.操做系統如何進行分頁調度?--要考LRU

1.最講置換原則-OPT 2.先進先出原則-FIFO 3.最近最少使用置換算法-LRU 4.時鐘置換算法

//擴展一下LinkedHashMap這個類,讓他實現LRU算法
class LRULinkedHashMap<K,V> extends LinkedHashMap<K,V>{
    //定義緩存的容量
    private int capacity;
    private static final long serialVersionUID = 1L;
    //帶參數的構造器
    LRULinkedHashMap(int capacity){
        //調用LinkedHashMap的構造器,傳入如下參數
        super(16,0.75f,true);
        //傳入指定的緩存最大容量
        this.capacity=capacity;
    }
    //實現LRU的關鍵方法,若是map裏面的元素個數大於了緩存最大容量,則刪除鏈表的頂端元素
    @Override
    public boolean removeEldestEntry(Map.Entry<K, V> eldest){
        System.out.println(eldest.getKey() + "=" + eldest.getValue());
        return size()>capacity;
    }
}

複製代碼

42.講講LinkedHashMap

Java8 LinkedHashMap工做原理及實現

LinkedHashMap是經過哈希表和鏈表實現的,它經過維護一個鏈表來保證對哈希表迭代時的有序性,而這個有序是指鍵值對插入的順序。

LinkedHashMap 的大體實現以下圖所示,固然鏈表和哈希表中相同的鍵值對都是指向同一個對象,這裏把它們分開來畫只是爲了呈現出比較清晰的結構。

image.png

LinkedHashMap是Hash表和鏈表的實現,而且依靠着雙向鏈表保證了迭代順序是插入的順序。

三個重點實現的函數

在HashMap中提到了下面的定義:

// Callbacks to allow LinkedHashMap post-actions
//1.把當前節點e移至鏈表的尾部。由於使用的是雙向鏈表,因此在尾部插入能夠以O(1)的時間複雜度來完成。而且只有當accessOrder設置爲true時,纔會執行這個操做。在HashMap的putVal方法中,就調用了這個方法。
void afterNodeAccess(Node<K,V> p) { }
//2.afterNodeInsertion方法是在哈希表中插入了一個新節點時調用的,它會把鏈表的頭節點刪除掉,刪除的方式是經過調用HashMap的removeNode方法。經過afterNodeInsertion方法和afterNodeAccess方法,是否是就能夠簡單的實現一個基於最近最少使用(LRU)的淘汰策略了?固然,咱們還要重寫removeEldestEntry方法,由於它默認返回的是false。
void afterNodeInsertion(boolean evict) { }
//3.這個方法是當HashMap刪除一個鍵值對時調用的,它會把在HashMap中刪除的那個鍵值對一併從鏈表中刪除,保證了哈希表和鏈表的一致性。
void afterNodeRemoval(Node<K,V> p) { }
複製代碼

LinkedHashMap繼承於HashMap,所以也從新實現了這3個函數,顧名思義這三個函數的做用分別是:節點訪問後、節點插入後、節點移除後作一些事情。

43.線程同步與阻塞的關係?同步必定阻塞嗎?阻塞必定同步嗎?,同步和異步有什麼區別?

同步與非同步:主要是保證互斥的訪問臨界資源的狀況 阻塞與非阻塞:主要是從 CPU 的消耗上來講的

44.int與Integer的區別,分別什麼場合使用

一、Integer是int提供的封裝類,而int是Java的基本數據類型
二、Integer默認值是null,而int默認值是0;
三、聲明爲Integer的變量須要實例化,而聲明爲int的變量不須要實例化;
四、Integer是對象,用一個引用指向這個對象,而int是基本類型,直接存儲數值。

複製代碼

int是基本數據類型,Integer是包裝類,相似HashMap這樣的結構必須使用包裝類,由於包裝類繼承自Object,都須要實現HashCode,因此可使用在HashMap這類數據結構中。

45.RPC的詳細過程

RPC主要的重點有: 動態代理,主要是invoke反射原理 序列化,使用Thrift的效率高 通訊方式,使用NettyNIO能提升效率 服務發現,使用zookeeper能夠實現

  • 1)服務消費方(client)調用以本地調用方式調用服務;
  • 2)client stub接收到調用後負責將方法、參數等組裝成可以進行網絡傳輸的消息體;
  • 3)client stub找到服務地址,並將消息發送到服務端;
  • 4)server stub收到消息後進行解碼;
  • 5)server stub根據解碼結果調用本地的服務;
  • 6)本地服務執行並將結果返回給server stub;
  • 7)server stub將返回結果打包成消息併發送至消費方;
  • 8)client stub接收到消息,並進行解碼;
  • 9)服務消費方獲得最終結果。
相關文章
相關標籤/搜索