相關知識點

1:tcp/ip協議

2:Socket原理

3:多進程相關的

4:View事件分發源碼

5:HashMap源碼,原理,增刪的情況後端數據結構如何位移,如何變得線程安全,每種方式的優缺點

HashMap內部通過維護一個Entry<K, V>數組(變量爲table),來實現其基本功能,而Entry<K, V>是HashMap的內部類,其主要作用便是存儲鍵值對,其數據結構大致如下圖所示。

從Entry的數據結構可以看出,多個Entry是可以形成一個單向鏈表的,HashMap中維護的Entry<K, V>數組(之後簡稱爲Entry數組,或table,容易區分)其實就是存儲的一系列Entry<K, V>鏈表的表頭。那麼HashMap中存儲數據table數組的數據結構,大致可以如下圖所示(假設只有部分數據)。

注:Entry數組的默認長度爲16,負載因子爲0.75。

將上圖中的每一行,稱爲桶(bucket),那麼table的索引便是bucketIndex。而HashMap中的插入、獲取、刪除等操作最主要的便是對table和桶(bucket)的操作。下面將主要通過插入操作,看其數據結構的變化

在整個插入操作中,有一個很重要的操作,便是對table數組擴容,擴容的算法相對簡單,但是在多線程下它卻容易引發一個線程安全的問題。

注:擴容需要會把原先table中的值移動到新的數組中,再賦值給table變量,一個合適的初始大小和負載因子能夠提高效率。

4. 線程不安全

在多線程環境下,假設有容器map,其存儲的情況如下圖所示(淡藍色爲已有數據)。

此時的map已經達到了擴容閾值12(16 * 0.75 = 12),而此時線程A與線程B同時對map容器進行插入操作,那麼都需要擴容。此時可能出現的情況如下:線程A與線程B都進行了擴容,此時便有兩個新的table,那麼再賦值給原先的table變量時,便會出現其中一個newTable會被覆蓋,假如線程B擴容的newTable覆蓋了線程A擴容的newTable,並且是在A已經執行了插入操作之後,那麼就會出現線程A的插入失效問題,也即是如下圖中的兩個table只能有一個會最後存在,而其中一個插入的值會被捨棄的問題。

 

這便是HashMap的線程不安全性,當然這只是其中的一點。而要消除這種隱患,則可以加鎖或使用HashTable和ConcurrentHashMap這樣的線程安全類,但是HashTable不被建議使用,推薦使用ConcurrentHashMap容器。

從ConcurrentHashMap代碼中可以看出,它引入了一個“分段鎖”的概念,具體可以理解爲把一個大的Map拆分成N個小的HashTable,根據key.hashCode()來決定把key放到哪個HashTable中。

在ConcurrentHashMap中,就是把Map分成了N個Segment,put和get的時候,都是現根據key.hashCode()算出放到哪個Segment中:

ConcurrentHashMap中默認是把segments初始化爲長度爲16的數組。

根據ConcurrentHashMap.segmentFor的算法,3、4對應的Segment都是segments[1],7對應的Segment是segments[12]。

(1)Thread1和Thread2先後進入Segment.put方法時,Thread1會首先獲取到鎖,可以進入,而Thread2則會阻塞在鎖上:

(2)切換到Thread3,也走到Segment.put方法,因爲7所存儲的Segment和3、4不同,因此,不會阻塞在lock()

以上就是ConcurrentHashMap的工作機制,通過把整個Map分爲N個Segment(類似HashTable),可以提供相同的線程安全,但是效率提升N倍,默認提升16倍。

 

6:android動畫原理

7:虛擬機相關

8:Object類你知道的方法

clone()

clode()方法又是一個被聲明爲native的方法,因此,我們知道了clone()方法並不是Java的原生方法,具體的實現是有C/C++完成的。clone英文翻譯爲”克隆",其目的是創建並返回此對象的一個副本。形象點理解,這有一輛科魯茲,你看着不錯,想要個一模一樣的。你調用此方法即可像變魔術一樣變出一輛一模一樣的科魯茲出來。配置一樣,長相一樣。但從此刻起,原來的那輛科魯茲如果進行了新的裝飾,與你克隆出來的這輛科魯茲沒有任何關係了。你克隆出來的對象變不變完全在於你對克隆出來的科魯茲有沒有進行過什麼操作了。Java術語表述爲:clone函數返回的是一個引用,指向的是新的clone出來的對象,此對象與原對象分別佔用不同的堆空間。

getClass();

getClass()也是一個native方法,返回的是此Object對象的類對象/運行時類對象Class<?>。效果與Object.class相同。

wait():

調用此方法所在的當前線程等待,直到在其他線程上調用此方法的主調(某一對象)的notify()/notifyAll()方法。

notify()/notifyAll():

喚醒在此對象監視器上等待的單個線程/所有線程。

 

finalize()

protected void finalize() throws Throwable {} 對象的"遺言"方法 當gc回收一個對象的時候,主動會調用這個對象的finalize方法 面試題:final和finalize之間的區別? final表示最終的 修飾符,可以修飾類、方法、變量 finalize是Object類裏面的一個方法,當gc回收一個對象的時候會主動調用的一個方法

toString()

public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } 用來制定打印一個對象,顯示的內容 當打印一個對象時,實際上打印的是該對象的toString方法,Object類本身打印對象,顯示 類型@XX

equals()

public boolean equals(Object obj) { return (this == obj); } Object類本身比較的是兩個對象的地址 程序員可以按照自己的意願將內存裏面不同的兩個對象視爲相等對象 -> 邏輯相等也就是說,制定一個類型比較的規則,當什麼條件成立的時候,可以將兩個不同的對象視爲相等對象

hashCode()

public native int hashCode(); 制定一個對象的散列特徵碼 

9:你重寫過hashcode和equals麼,要注意什麼,爲什麼要重寫hashcode和equals

閱讀HashMap的源碼,我們可以看到,HashMap中實現了一個Entry[]數組,數組的每個item是一個單項鍊表的結構,當我們put(key, value)的時候,HashMap首先會newItem.key.hashCode()作爲該newItem在Entry[]中存儲的下標,要是對應的下標的位置上沒有任何item,則直接存儲上去,要是已經有oldItem存儲在了上面,那就會判斷oldItem.key.equals(newItem.key),那麼要是我們把上面的Person作爲key進行存儲的時候,重寫了equals()方法,但沒重寫hashCode()方法,當我們去put()的時候,首先會通過hashCode() 計算下標,由於沒有重寫hashCode(),那麼實質是完整的Object的hashCode(),會受到Object多個屬性的影響,本來equals的兩個Person對象,反而得到了兩個不同的下標。

同樣的,HashMap在get(key)的過程中,也是首先調用hashCode()計算item的下標,然後在對應下標的地方找,要是爲null,就返回null,要是 != null,會去調用equals()方法,比較key是否一致,只有當key一致的時候,纔會返回value,要是我們沒有重寫hashCode()方法,本來有的item,反而會找不到,返回null結果。

 

 

10:講一下穩定的排序和不穩定的排序

https://blog.csdn.net/ithomer/article/details/5636226

排序算法的穩定性大家應該都知道,通俗地講就是能保證排序前2個相等的數其在序列的前後位置順序和排序後它們兩個的前後位置順序相同

 

11:講一下快速排序的思想

 

 

 

12:說一下jvm內存模型吧,有哪些區,分別幹什麼的

Java 內存模型中將 JVM 分爲堆和棧:

 

堆爲同一個 JVM 中所有線程共享,存放運行時創建的對象和數組數據;

棧爲每個線程獨有,棧中存放了當前方法的調用信息以及基本數據類型和引用類型的數據。

Java 中的堆

堆在虛擬機啓動時創建,堆佔用的內存由垃圾回收器管理,不需要我們手動回收。

JVM 沒有規定死必須使用哪一種內存回收機制,不同的虛擬機實現可以使用不同的回收算法。

堆中包含在 Java 程序中創建的所有對象,無論是哪一個線程創建的。

一個對象的成員變量隨着這個對象自身存放在堆上。不管這個成員變量是基本類型還是引用類型。

Java 中的棧

棧在線程創建時創建,它和 C 語言中的棧相似,在一個方法中,你創建的局部變量和部分結果都會保存在棧中,並在方法調用和返回中起作用。

當前棧只對當前線程可見。即使兩個線程執行同樣的代碼,這兩個線程仍然會在自己的線程棧中創建一份本地副本。

因此,每個線程擁有每個本地變量的獨有版本。

棧中保存方法調用棧、基本類型的數據、以及對象的引用。

計算機中的內存、寄存器、緩存

這部分摘自:http://ifeve.com/java-memory-model-6/

一個現代計算機通常由兩個或者多個 CPU,每個 CPU 都包含一系列的寄存器,CPU 在寄存器上執行操作的速度遠大於在主存上執行的速度。

每個 CPU 可能還有一個 CPU 緩存層。CPU 訪問緩存層的速度快於訪問主存的速度,但通常比訪問內部寄存器的速度還要慢一點。

        通常情況下,當一個 CPU 需要讀取主存時,它會將主存的部分讀到 CPU 緩存中。它甚至可能將緩存中的部分內容讀到它的內部寄存器中,然後在寄存器中執行操作。

        當 CPU 需要將結果寫回到主存中去時,它會將內部寄存器的值刷新到緩存中,然後在某個時間點將值刷新回主存。

 

多線程可能出現的問題

通過上述介紹,我們可以知道,如果多個線程共享一個對象,每個線程在自己的棧中會有對象的副本。

如果線程 A 對對象中的某個變量進行修改後還沒來得及寫回主存,線程 B 也對該變量進行了修改,那最後刷新回主內存後的值一定和期望的值不一致。

就好比拭心和小翔同時開發同一模塊代碼,拭心下筆如有神不一會兒搞定了註冊登錄並且提交,小翔沒有從服務器拉代碼就矇頭狂寫,最後一 pull 代碼,就會發現自己寫的好多都跟服務器上的衝突了!

競態條件與臨界區

當多個線程操作同一資源時,如果對資源的訪問順序敏感,就稱存在競態條件。導致競態條件發生的代碼區稱作臨界區。

在臨界區中使用適當的同步就可以避免競態條件,比如 synchronized, 顯式鎖和原子操作類等。

內存可見性

拭心寫的代碼小翔無法立即看到,這就是所謂的“內存可見性”問題。

爲了讓線程 A 對變量做的修改線程 B 立即可以看到,我們可以使用 volatile 修飾變量或者對修改操作

 

 

 

13:說一下gc算法,垃圾回收,分代回收說下

https://blog.csdn.net/paul_wei2008/article/details/55259579

 

 

 

 

14:TCP連接中的三次握手和四次揮手,四次揮手的最後一次ack的作用是什麼,爲什麼要time wait,爲什麼是2msl

爲什麼TIME_WAIT狀態還需要等2MSL後才能返回到CLOSED狀態
因爲雖然雙方都同意關閉連接了,而且握手的4個報文也都發送完畢,按理可以直接回到CLOSED 狀態(就好比從SYN_SENT 狀態到ESTABLISH 狀態那樣),但是我們必須假想網絡是不可靠的,你無法保證你(客戶端)最後發送的ACK報文一定會被對方收到,就是說對方處於LAST_ACK 狀態下的SOCKET可能會因爲超時未收到ACK報文,而重發FIN報文,所以這個TIME_WAIT 狀態的作用就是用來重發可能丟失的ACK報文。

15:遞歸求出所有的路徑

16:設計模式講一下熟悉的

17:會不會濫用設計模式

18:多線程的條件變量,爲什麼要在while體裏

19:手撕算法:反轉單鏈表

 

遞歸法:

public class ReverseLink {

    public static void main(String[] args) {

        Node node = readyNote();

        reverseLinkedList(node);

    }

    public static Node readyNote() {

        Node node1 = new Node();

        node1.data = 1;

        Node node2 = new Node();

        node2.data = 2;

        Node node3 = new Node();

        node3.data = 3;

        Node node4 = new Node();

        node4.data = 4;

        Node node5 = new Node();

        node5.data = 5;

        Node node6 = new Node();

        node6.data = 6;

        node1.next = node2;

        node2.next = node3;

        node3.next = node4;

        node4.next = node5;

        node5.next = node6;

        return node1;

    }


    static Node reverseLinkedList(Node node) {

        if (node == null || node.next == null) {

            return node;

        } else {

            Node headNode = reverseLinkedList(node.next);

            node.next.next = node;

            node.next = null;

            return headNode;

        }

    }

}

public class Node {

    Integer data;

    Node next;

}

20:實現以下類似微博子結構的數據結構,輸入一系列父子關係,輸出一個類似微博評論的父子結構圖

21:首先java多線程

22:手寫java的socket變成,服務端和客戶端

23:手撕算法:爬樓梯,寫出狀態轉移方程

24:時針分針什麼時候重合

25:java中的多態

26:JVM參數你知道的說一下

Xmx4608M dalvik.vm.heapsize

-XX:ParallelGCThreads=<N> 整數。並行GC的線程個數

 

27:手撕一個單例模式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章