java面試總結

1、volatile的作用?
Java語言中的volatile變量可以被看做是一種“程度較輕的synchronized”,鎖提供了兩種主要特性:互斥性和可見性,volatile變量具有synchronized的可見性特性。這就是說線程能夠自動發現volatile變量的最新值。
2、BeanFactory和ApplicationContext的區別?
spring自帶了幾種容器實現,可以歸爲兩種不同的類型:(1)BeanFactory是最簡單的容器,提供基本的DI(Dependence Injection,依賴注入)。(2)ApplicationContext基於BeanFactory之上構建,並提供面向應用的服務。
3、ArrayList,LinkedList的區別?
(1)ArrayList的實現是基於動態數組的數據結構,LinkedList的實現基於鏈表的數據結構。
(2)對於隨機訪問get和set,ArrayList覺得優於LinkedList,因爲LinkedList要移動指針。
(3)對於新增和刪除操作add和remove,LinedList比較佔優勢,因爲ArrayList要移動數據。
ArrayList.java和LinkedList.java:

//ArrayList的部分源碼
public class ArrayList<E> extends AbstractList<E>{
    //初始化數組大小爲10
    private static final int DEFAULT_CAPACITY = 10;
    //該Object數組用於存放數據
    private transient Object[] elementData;
    //定義動態數組的大小
    private int size;
    //擴容函數
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        //位移運算效率更高,擴容爲1.5倍
        //Vector容器可以設置擴容量,默認是2倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    public E remove(int index) {
        E oldValue = elementData(index);
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,numMoved);
        elementData[--size] = null;  
        return oldValue;
    }
}

//LinkedList的部分源碼
public class LinkedList<E> extends AbstractSequentialList<E>{
    transient int size = 0;
    //Pointer to first node.
    transient Node<E> first;
    //Pointer to last node.
    transient Node<E> last;

    Node<E> node(int index) {
        //如果index在前半部分,通過前往後找
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {//如果index在後半部分,通過後往前找
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
    private static class Node<E> {//靜態內部類
        E item;
        Node<E> next;
        Node<E> prev;
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
}

4、線程池
java.uitl.concurrent.ThreadPoolExecutor類是線程池中最核心的一個類。

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {}

參數說明:
corePoolSize:核心池大小,在創建了線程池後,默認情況下,線程池中並沒有任何線程,而是等待有任務到來才創建線程去執行任務,除非調用了prestartAllCoreThreads()或者prestartCoreThread()方法,從這2個方法的名字就可以看出,是預創建線程的意思,即在沒有任務到來之前就創建corePoolSize個線程或者一個線程。默認情況下,在創建了線程池後,線程池中的線程數爲0,當有任務來之後,就會創建一個線程去執行任務,當線程池中的線程數目達到corePoolSize後,就會把到達的任務放到緩存隊列當中;
maximumPoolSize:線程池最大線程數,這個參數也是一個非常重要的參數,它表示在線程池中最多能創建多少個線程;
keepAliveTime:表示線程沒有任務執行時最多保持多久時間會終止。默認情況下,只有當線程池中的線程數大於corePoolSize時,keepAliveTime纔會起作用,直到線程池中的線程數不大於corePoolSize,即當線程池中的線程數大於corePoolSize時,如果一個線程空閒的時間達到keepAliveTime,則會終止,直到線程池中的線程數不超過corePoolSize。但是如果調用了allowCoreThreadTimeOut(boolean)方法,在線程池中的線程數不大於corePoolSize時,keepAliveTime參數也會起作用,直到線程池中的線程數爲0;
unit:參數keepAliveTime的時間單位,有7種取值,包括天時分秒等;
workQueue:一個阻塞隊列,用來存儲等待執行的任務,主要有三種:

ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;

threadFactory:線程工廠,主要用來創建線程;
handler:表示當拒絕處理任務時的策略,有以下四種取值:

ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。 
ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。 
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)
ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務 

在ThreadPoolExecutor類中有幾個非常重要的方法:
execute()方法實際上是Executor中聲明的方法,在ThreadPoolExecutor進行了具體的實現,這個方法是ThreadPoolExecutor的核心方法,通過這個方法可以向線程池提交一個任務,交由線程池去執行。

submit()方法是在ExecutorService中聲明的方法,在AbstractExecutorService就已經有了具體的實現,在ThreadPoolExecutor中並沒有對其進行重寫,這個方法也是用來向線程池提交任務的,但是它和execute()方法不同,它能夠返回任務執行的結果,去看submit()方法的實現,會發現它實際上還是調用的execute()方法,只不過它利用了Future來獲取任務執行結果(Future相關內容將在下一篇講述)。

shutdown()和shutdownNow()是用來關閉線程池的。

如果當前線程池中的線程數目小於corePoolSize,則每來一個任務,就會創建一個線程去執行這個任務;
如果當前線程池中的線程數目>=corePoolSize,則每來一個任務,會嘗試將其添加到任務緩存隊列當中,若添加成功,則該任務會等待空閒線程將其取出去執行;若添加失敗(一般來說是任務緩存隊列已滿),則會嘗試創建新的線程去執行這個任務;
如果當前線程池中的線程數目達到maximumPoolSize,則會採取任務拒絕策略進行處理;
如果線程池中的線程數量大於 corePoolSize時,如果某線程空閒時間超過keepAliveTime,線程將被終止,直至線程池中的線程數目不大於corePoolSize;如果允許爲核心池中的線程設置存活時間,那麼核心池中的線程空閒時間超過keepAliveTime,線程也會被終止。

在java doc中,並不提倡我們直接使用ThreadPoolExecutor,而是使用Executors類中提供的幾個靜態方法來創建線程池:

Executors.newCachedThreadPool();        //創建一個緩衝池,緩衝池容量大小爲Integer.MAX_VALUE
Executors.newSingleThreadExecutor();   //創建容量爲1的緩衝池
Executors.newFixedThreadPool(int);    //創建固定容量大小的緩衝池

更詳細的請參考:Java併發編程:線程池的使用
5、ThreadLocal什麼情況下會發生內存泄露?
ThreadLocal,很多地方叫做線程本地變量,也有些地方叫做線程本地存儲,其實意思差不多。可能很多朋友都知道ThreadLocal爲變量在每個線程中都創建了一個副本,那麼每個線程可以訪問自己內部的副本變量。
引用關係(實線代表強引用,虛線代表弱引用)
每個Thread都有一個Map,類型是ThreadLocal.ThreadLocalMap,Map的key是ThreadLocal實例,這個Map使用了弱引用,只是針對key,原註解是這樣的:the hash table entries use WeakReferences for keys.當把ThreadLocal實例設爲null時,ThreadLocal實例會被回收,也就是key被回收,但是value不能被回收,因爲存在CurrentThread連接過來的強引用,因此只有當CurrentThread結束以後,value才被GC回收。這塊value在CurrentThread結束前,永遠不會被訪問到了. 所以存在着內存泄露. 最好的做法是將調用Threadlocal實例的remove方法。
6、TCP三次握手,四次揮手
這裏寫圖片描述
(1)第一次握手:Client將標誌位SYN置爲1,隨機產生一個值seq=J,並將該數據包發送給Server,Client進入SYN_SENT狀態,等待Server確認。
(2)第二次握手:Server收到數據包後由標誌位SYN=1知道Client請求建立連接,Server將標誌位SYN和ACK都置爲1,ack=J+1,隨機產生一個值seq=K,並將該數據包發送給Client以確認連接請求,Server進入SYN_RCVD狀態。
(3)第三次握手:Client收到確認後,檢查ack是否爲J+1,ACK是否爲1,如果正確則將標誌位ACK置爲1,ack=K+1,並將該數據包發送給Server,Server檢查ack是否爲K+1,ACK是否爲1,如果正確則連接建立成功,Client和Server進入ESTABLISHED狀態,完成三次握手,隨後Client與Server之間可以開始傳輸數據了。
這裏寫圖片描述
(1)第一次揮手:Client發送一個FIN,用來關閉Client到Server的數據傳送,Client進入FIN_WAIT_1狀態。
(2)第二次揮手:Server收到FIN後,發送一個ACK給Client,確認序號爲收到序號+1(與SYN相同,一個FIN佔用一個序號),Server進入CLOSE_WAIT狀態。
(3)第三次揮手:Server發送一個FIN,用來關閉Server到Client的數據傳送,Server進入LAST_ACK狀態。
(4)第四次揮手:Client收到FIN後,Client進入TIME_WAIT狀態,接着發送一個ACK給Server,確認序號爲收到序號+1,Server進入CLOSED狀態,完成四次揮手。
7、ReentrantLock與Synchronized的區別
ReentrantLock 擁有Synchronized相同的併發性和內存語義,此外還多了 鎖投票,定時鎖等候和中斷鎖等候
線程A和B都要獲取對象O的鎖定,假設A獲取了對象O鎖,B將等待A釋放對O的鎖定,
如果使用 synchronized ,如果A不釋放,B將一直等下去,不能被中斷
如果 使用ReentrantLock,如果A不釋放,可以使B在等待了足夠長的時間以後,中斷等待,而幹別的事情。
ReentrantLock獲取鎖定與三種方式:
a) lock(), 如果獲取了鎖立即返回,如果別的線程持有鎖,當前線程則一直處於休眠狀態,直到獲取鎖
b) tryLock(), 如果獲取了鎖立即返回true,如果別的線程正持有鎖,立即返回false;
c)tryLock(long timeout,TimeUnit unit), 如果獲取了鎖定立即返回true,如果別的線程正持有鎖,會等待參數給定的時間,在等待的過程中,如果獲取了鎖定,就返回true,如果等待超時,返回false;
d) lockInterruptibly:如果獲取了鎖定立即返回,如果沒有獲取鎖定,當前線程處於休眠狀態,直到或者鎖定,或者當前線程被別的線程中斷
在資源競爭不是很激烈的情況下,Synchronized的性能要優於ReetrantLock,但是在資源競爭很激烈的情況下,Synchronized的性能會下降幾十倍,但是ReetrantLock的性能能維持常態

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