2019/03面試題-併發爲主

14日面試題:

問題1:線程類的構造方法,靜態代碼塊是被哪一個線程調用的?

解析:

  首先提到了線程類,那麼在java中建立(實現)線程是有三種方式的:html

  實現interface Runnable的run()  繼承class Thread override run()  使用FutureTask的方式(此方式有返回值,可是實際上仍是要依賴於Thread類)java

  補充FutureTask方式:面試

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class Main {
    public static void main(String[] args) {
        FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
        new Thread(futureTask).start();

        try {
            String result = futureTask.get();
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class CallerTask implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "i am a baby";
    }
}
View Code

 

  綜合上面三種方式,結合問題的構造方法,那麼答案應該就是Thread的構造方法了。編程

  

  提到靜態代碼塊:先找到靜態代碼塊的概念:安全

  在Java中使用static和{}聲明的代碼塊就是靜態代碼塊。多線程

  查了半天也沒找到很明確的指向:看到羣友說百度到了:靜態代碼塊是被new這個線程類所在線程所調用的。併發

  靜態代碼塊是在類加載的時候調用的,且類加載器是缺省規則,那麼調用靜態代碼塊的線程就是調用當前類加載器的線程  參考文檔dom

問題2:同步方法和同步代碼塊,哪一個是更好的選擇?

  解析:

  同步方法:在方法上加入關鍵字:synchronized的方法異步

  同步代碼塊:在代碼塊上加入關鍵字:synchronized(Object){}  (小括號內即爲加鎖的對象)ide

  選用同步代碼塊好,由於能夠只將可能出現併發問題的代碼放入同步代碼塊中,且同步代碼塊能夠自行選擇加鎖的對象。同步方法的鎖對象是當前類的實例

問題3:如何檢測死鎖,怎麼預防死鎖?

  解析:

  死鎖:兩個或者兩個以上的線程在執行過程當中,由於爭奪資源而形成的互相等待的現象。(鎖已經沒有人拿了,可是如今全部的線程都在等待拿鎖,而不是去拿鎖)

  死鎖的四個必要條件:

  互斥條件:資源只能被一個線程佔用,若資源已經被佔用,其餘線程須要等待知道佔用線程釋放資源。

  請求並持有條件:一個線程已經佔用至少一個資源,又提出新的資源佔用,可是新資源被其餘線程佔用,因此當前線程被阻塞,且不會釋放已經佔用的資源。

  不可剝奪條件:線程佔用資源後,在使用完以前是不會釋放的,且不可能被其餘線程奪走。

  環路等待條件:發生死鎖時,必然存在一個線程——資源的環形鏈。(請求並持有條件串成環既是)。

  避免死鎖須要破壞至少一個死鎖的必要條件便可,實際上只有請求並持有和環路等待是能夠破壞的。

  那麼如何破壞請求並持有條件和環路條件呢?實際上經過資源申請的有序性就能夠實現。(反過來理解也能夠認爲是由於資源申請的有序性沒法獲得保障才致使的請求並持有和環路等待)


15日面試題 

問題1:線程池做用,主要實現類,並說出實現類場景以及區別。

解析:

  線程池的做用跟解決的問題實際上是一個概念:

  解決兩個問題:

    一:大量異步任務時,線程池可以提供更好的性能:線程可重複利用,避免建立線程和銷燬線程帶來的資源損耗。

    二:線程池提供了一種資源限制和管理的手段,能夠限制線程的個數,也可動態新增線程池。

  線程池以及場景和區別:

    1.newFixedThreadPool:建立了一個核心線程個數和最大線程個數都爲nThreads的線程池,且其阻塞隊列長度爲Integer.MAX_VAlUE。其keepAliveTime=0說明只要線程個數比核心線程個數多而且當前空閒就回收。注:關於這個keepAliveTime=0的回收概念,我我的暫時有點不理解(由於核心線程數和最大線程數是一個值)。

    2.newSingleThreadExecutor:建立一個核心線程個數和最大線程個數都爲1的線程池,且阻塞隊列長度爲Integer.Max_VALUE。keepAliveTime=0說明線程個數比核心線程個數多且當前空閒就回收。

    3.newCachedThreadPool:建立一個按需建立線程的線程池,初始線程個數爲0,最多線程個數爲Integer.MAX_VALUE,而且阻塞隊列爲同步隊列,keepAliveTime=60說明只要當前線程在60s內空閒則回收。這個類型特殊在加入同步隊列的任務會被立刻執行,同步隊列中最多隻有一個任務。

    4.ScheduledThreadPoolExecutor:主要是指定必定延遲時間後或者定時執行任務調度執行的線程池。

問題2:ThreadPoolExecutor使用場景,以及原理。

解析

  實現原理:當向線程池提交一個任務後,線程池進行處理:

    一:線程池判斷線程池的線程是否都在執行任務,若是不是,則建立一個新的工做線程來執行任務(使用threadFactory建立),若是線程都在執行任務,進入下個流程。

    二:線程池判斷工做隊列是否滿了,若是沒有,則將新提交的任務存儲在工做隊列中(對應構造函數中的workQueue),若是滿了進入下個流程。

    三:線程池判斷線程池是否已經滿了(線程已經達到最大數,且任務隊列已經滿了),若是沒有,則建立一個新的工做線程執行任務,若是滿了,任務將被拒絕並交給飽和策略來處理這個任務。

  應用場景其實就是問題一種的前三個線程池的應用場景:

    一:FixedThreadPool:保證全部任務都執行,永遠不會拒絕新任務;缺點是:隊列數量沒有限制,在任務執行無限延長的極端狀況下可能形成內存問題。

    二:SingleThreadExecutor:適用於邏輯上須要單線程的場景,同時無解的LinkedBlockingQueue保證新任務都可以放入隊列,不會被拒絕,缺點也是可能會形成內存問題。

    三:CachedThreadPool:提交的任務會當即分配線程執行,線程的數量會隨着任務數進行擴展和縮減,問題是可能會建立過多的線程。

問題3:Executor拒絕策略說的是什麼?

解析

  一共四中拒絕策略:

    一:ThreadPoolExecutor.AbortPolicy  拋棄任務並拋出RejectedExecutionException

    二:ThreadPoolExecutor.DiscardPolicy  拋棄任務,可是不拋出異常

    三:ThreadPoolExecutor.DiscardOldestPlicy  拋棄隊列最前面的任務,執行後面的任務

    四:ThreadPoolExecutor.callerRunsPolicy   由調用線程處理該任務

問題4:無界阻塞延遲隊列的delayqueue原理是什麼?

解析

  是一個支持延期獲取元素的無界限隊列,隊列使用PriorityQueue實現,隊列中的元素必須實現Delayed接口,建立元素時能夠指定多久才能從隊列中獲取當前元素,只有在延時期滿時才能從隊列中獲取元素。

  PriorityQueue是一個優先級的隊列,隊列中的元素會按照優先級進行排序。

  詳解

問題5:CyclicBarrier和CountDownLatch的區別?

解析

  CountDownLatch:能夠實現計數器功能,使線程按照必定執行順序執行。

  CyclicBarrier:可讓一組線程等待至某個狀態以後再所有執行,當全部等待線程被釋放後,CyclicBarrier可以重複使用

  詳解

15日反思:關於併發方面差的東西有點多,最近正在補,目前看的書是《Java併發編程之美》,但願能儘快吸取消化進步。

爭取可以回頭再把問題答案從新整理一遍


 16日面試題

問題1:Java中的同步集合與併發集合有什麼區別

解析:

  同步集合:

    - Hashtable

    - Vector

    - 同步集合包裝類:Collections下面的方法能夠獲取線程安裝的同步集合:

      synchronizedCollection(Collection<T> c)

      synchronizedList(List<T> list)

      synchronizedMap(Map<k, v> m)

      synchronizedSet(Set<T> set)

      synchronizedSortedMap(SortedMap<k ,v> sm)

    同步集合的優缺點:在單線程環境下能夠保證數據安全,可是是經過synchronized關鍵字實現同步方法將訪問操做串行化,致使在併發環境中效率下降。且在多線程環境下的複合操做是非線程安全的,須要加鎖來實現。

  併發集合:

    Java提供了兩類的併發集合:

      - 阻塞式集合(blocked collection):這類集合包含添加和移除的方法,當集合已滿或者爲空時被調用的添加或者移除方法就不能馬上執行,那麼調用這個方法的線程將會被阻塞,一直到改方法被調用成功。

      - 非阻塞式集合(Non-blocked collection): 這類集合包含添加和移除方法,若是集合已滿或者爲空時,調用添加或移除方法會返回null或者報錯,可是調用這個方法的線程不會被阻塞。

    常見的併發集合包括:

      - 非阻塞列表對應的實現類:ConcurrentLinkedDeque

      - 阻塞式列表對於的實現類:LinkedBlockingDeque

      - 用於數據生成或者消費的阻塞式列表對應的實現類:LinkedTransferQueue

      - 按照優先級排序列表元素的阻塞式列表對應的實現類:PriorityBlockingQueue

      - 帶有延遲列表元素的阻塞式列表對應的實現類:DelayQueue

      - 非阻塞式列表可遍歷映射對應的實現類ConcurrentSkipListMap

      - 隨機數字對應的實現類:ThreadLockRandom

      - 原子變量對應的實現類:AtomicLong和AtomicIntegerArray

問題2:java中invokeAndWait和invokeLater有什麼區別?

解析:

  事件派發線程是swing的組件,swing是事件驅動的,全部的操做都須要事件派發線程去完成,可是有些很費事的操做不太好放到事件派發線程中去。

  SwingUtils提供了兩個方法:invokeAndWait和invokeLater,他們都是事件派發線程可運行的對象,當對象位於事件派發隊列的隊首時,他們就被執行其中的run(),方法是容許事件派發線程調用另外一個線程中的任意一個方法。

  invokeAndWait和invokeLater均可將可運行對象放到事件派發隊列中去,可是invokeLater將對象放入隊列就返回了,invokeAndWait將對象放入後直到已啓動了可運行的run()時才返回。

問題3:什麼是FutureTask

解析:

  Runnable是一個接口,封裝了一個沒有參數和返回值異步執行的方法。

  Future也是一個接口,且其中保存異步計算的結果:

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
View Code

  FutureTask則同時實現了上述兩個接口

FutureTask內部提供了定義瞭如下變量:

  volatile int state:表示對象狀態,volatile關鍵字保證了內存可見性。futureTask中定義了7種狀態,表明了7種不一樣的執行狀態

private static final int NEW          = 0; //任務新建和執行中
private static final int COMPLETING   = 1; //任務將要執行完畢
private static final int NORMAL       = 2; //任務正常執行結束
private static final int EXCEPTIONAL  = 3; //任務異常
private static final int CANCELLED    = 4; //任務取消
private static final int INTERRUPTING = 5; //任務線程即將被中斷
private static final int INTERRUPTED  = 6; //任務線程已中斷

  Callable callable:被提交的任務

  Object outcome:任務執行結果或者任務異常

  volatile Thread runner:執行任務的線程

  volatile WaitNode waiters:等待節點,關聯等待線程

  long stateOffset:state字段的內存偏移量

  long runnerOffset:runner字段的內存偏移量

  long waitersOffset:waiters字段的內存偏移量 
  注:後三個字段是配合Unsafe類作CAS操做使用的。

 

建立一個futureTask對象task
提交task到調度器executor等待調度或者在另一個線程中執行task

等待調度中...

若是此時currentThread調取執行結果task.get(),會有幾種狀況
if task 尚未被executor調度或正在執行中
    阻塞當前線程,並加入到一個阻塞鏈表中waitNode
else if task被其它Thread取消,並取消成功 或task處於中斷狀態
    throw exception
else if task執行完畢,返回執行結果,或執行存在異常,返回異常信息
    
        
若是此時有另一個線程調用task.get()
    
執行過程同上

總結下,FutureTask的狀態流轉過程,能夠出現如下四種狀況: 
1. 任務正常執行並返回。 NEW -> COMPLETING -> NORMAL 
2. 執行中出現異常。NEW -> COMPLETING -> EXCEPTIONAL 
3. 任務執行過程當中被取消,而且不響應中斷。NEW -> CANCELLED 
4.任務執行過程當中被取消,而且響應中斷。 NEW -> INTERRUPTING -> INTERRUPTED

參考連接  參考連接

相關文章
相關標籤/搜索