JAVA面試常見問題之進程和線程篇

一、線程和進程的概念、並行和併發的概念html

  • 進程:計算機中的程序關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是操做系統結構的基礎。在早期面向進程設計的計算機結構中,進程是程序的基本執行實體;在當代面向線程設計的計算機結構中,進程是線程的容器。程序是指令、數據及其組織形式的描述,進程是程序的實體。
  • 線程:有時被稱爲輕量級進程(Lightweight Process,LWP),是程序執行流的最小單元。線程是程序中一個單一的順序控制流程。進程內一個相對獨立的、可調度的執行單元,是系統獨立調度和分派CPU的基本單位指運行中的程序的調度單位。在單個程序中同時運行多個線程完成不一樣的工做,稱爲多線程。

            

 

  • 並行:多個線程能夠同時執行,每個時間段,能夠有多個線程同時執行。

          

  • 併發多個線程同時競爭一個位置,競爭到的才能夠執行,每個時間段只有一個線程在執行。

         

 二、建立線程的方式及實現java

       建立線程的三種方式及實現代碼:面試

  1. 繼承Thread類建立
public class MyThread extends Thread{//繼承Thread類
  public void run(){
  //重寫run方法
  }
}

public class Main {
  public static void main(String[] args){
    new MyThread().start();//建立並啓動線程
  }
}

         2. 實現Runnable接口建立緩存

public class MyThread2 implements Runnable {//實現Runnable接口
  public void run(){
  //重寫run方法
  }
}

public class Main {
  public static void main(String[] args){
    //建立並啓動線程
    MyThread2 myThread=new MyThread2();
    Thread thread=new Thread(myThread);
    thread().start();
    //或者    new Thread(new MyThread2()).start();
  }
}

          3. 使用Callable和Futura建立安全

 三、進程間通訊的方式多線程

 進程間的通訊(IPC)經常使用方式管道(無名管道和命名管道)、消息隊列、信號量、共享存儲、套接字(Socket)及Streams等。其中 Socket和Streams支持不一樣主機上的兩個進程IPC。併發

  • 管道
    • 無名管道管道,一般指無名管道,是 UNIX 系統IPC最古老的形式。它是半雙工的,具備固定的讀端和寫端。只能用於親緣關係間的進程之間的通訊。
    • FIFO:也稱命名管道,它是一種文件類型。能夠在無關的進程之間交換數據。有路徑名與之相關聯,它以一種特殊設備文件形式存在於文件系統中。
  • 消息隊列是消息的連接表,存放在內核中。一個消息隊列由一個標識符(即隊列ID)來標識。特色
    • 消息隊列是面向記錄的,其中的消息具備特定的格式以及特定的優先級
    • 消息隊列獨立於發送與接收進程。進程終止時,消息隊列及其內容並不會被刪除。
    • 消息隊列能夠實現消息的隨機查詢,消息不必定要以先進先出的次序讀取,也能夠按消息的類型讀取。
  • 信號量:是一個計數器。信號量用於實現進程間的互斥與同步,而不是用於存儲進程間通訊數據。特色
    • 信號量用於進程間同步,若要在進程間傳遞數據須要結合共享內存
    • 信號量基於操做系統的 PV 操做,程序對信號量的操做都是原子操做。
    • 每次對信號量的 PV 操做不只限於對信號量值加 1 或減 1,並且能夠加減任意正整數。
    • 支持信號量組。
  • 共享存儲:指兩個或多個進程共享一個給定的存儲區。特色
    • 共享內存是最快的一種 IPC,由於進程是直接對內存進行存取。
    • 由於多個進程能夠同時操做,因此須要進行同步。
    • 信號量+共享內存一般結合在一塊兒使用,信號量用來同步對共享內存的訪問。
  • 總結
    • 無名管道:速度慢,容量有限,只有父子進程能通信
    • FIFO:任何進程間都能通信,但速度慢
    • 消息隊列:容量受到系統限制,且要注意第一次讀的時候,要考慮上一次沒有讀完數據的問題
    • 信號量:不能傳遞複雜消息,只能用來同步
    • 共享內存區:可以很容易控制容量,速度快,但要保持同步,好比一個進程在寫的時候,另外一個進程要注意讀寫的問題,至關於線程中的線程安全,固然,共享內存區一樣能夠用做線程間通信,不過沒這個必要,線程間原本就已經共享了同一進程內的一塊內存

四、說說 CountDownLatch、CyclicBarrier 原理和區別工具

   原理: 參考:https://blog.csdn.net/wantflydacheng/article/details/81664035
ui

    區別spa

 五、說說 Semaphore 工做原理,舉例說明

       Semaphore主要用於控制當前活動線程數目,就如同停車場系統通常,而Semaphore則至關於看守的人,用於控制總共容許停車的停車位的個數,而對於每輛車來講就如同一個線程,線程須要經過acquire()方法獲取許可,而release()釋放許可。若是許可數達到最大活動數,那麼調用acquire()以後,便進入等待隊列,等待已得到許可的線程釋放許可,從而使得多線程可以合理的運行

六、說說 Exchanger 原理

       Exchanger(交換者)是一個用於線程間協做的工具類。Exchanger用於進行線程間的數據交換。它提供一個同步點,在這個同步點兩個線程能夠交換彼此的數據。這兩個線程經過exchange方法交換數據, 若是第一個線程先執行exchange方法,它會一直等待第二個線程也執行exchange,當兩個線程都到達同步點時,這兩個線程就能夠交換數據,將本線程生產出來的數據傳遞給對方。所以使用Exchanger的重點是成對的線程使用exchange()方法,當有一對線程達到了同步點,就會進行交換數據。所以該工具類的線程對象是成對的。
       Exchanger類提供了兩個方法,String exchange(V x):用於交換,啓動交換並等待另外一個線程調用exchange;String exchange(V x,long timeout,TimeUnit unit):用於交換,啓動交換並等待另外一個線程調用exchange,而且設置最大等待時間,當等待時間超過timeout便中止等待。
       參考:https://blog.csdn.net/carson0408/article/details/79477280

七、ThreadLocal 原理分析,ThreadLocal爲何會出現OOM,出現的深層次原理

  • ThreadLocal是本地線程變量,用於保存某個線程的共享變量。方法包含get(),set(),remove(),initialValue()
  • 不正當的使用ThreadLocal時候會出現OOM,線程池的一個線程使用完ThreadLocal對象以後,不再用,因爲線程池中的線程不會退出,線程池中的線程的存在,同時ThreadLocal變量也會存在,佔用內存!形成OOM溢出!
  • 出現OOM的深層次原理 參考:https://mp.weixin.qq.com/s/xqb1kUVtD82JuvlqWGV18w   第二部分  因爲ThreadLocalMap的生命週期跟Thread同樣長,若是沒有手動刪除對應key就會致使內存泄漏。使用線程池,線程不會被銷燬,ThreadLocalMap也不會銷燬,隨着線程數量也來越多,ThreadLocalMap也會愈來愈多,佔得內存愈來愈大,從而致使出現內存溢出。

 

八、講講線程池的實現原理

一個線程集合workerSet和一個阻塞隊列workQueue。當用戶向線程池提交一個任務(也就是線程)時,線程池會先將任務放入workQueue中。workerSet中的線程會不斷的從workQueue中獲取線程而後執行。當workQueue中沒有任務的時候,worker就會阻塞,直到隊  列中有任務了就取出來繼續執行。

幾個重要參數意義:

  • corePoolSize: 規定線程池有幾個線程(worker)在運行。
  • maximumPoolSize: 當workQueue滿了,不能添加任務的時候,這個參數纔會生效。規定線程池最多隻能有多少個線程(worker)在執行。
  • keepAliveTime: 超出corePoolSize大小的那些線程的生存時間,這些線程若是長時間沒有執行任務而且超過了keepAliveTime設定的時間,就會消亡。
  • unit: 生存時間對於的單位
  • workQueue: 存聽任務的隊列
  • threadFactory: 建立線程的工廠
  • handler: 當workQueue已經滿了,而且線程池線程數已經達到maximumPoolSize,將執行拒絕策略。

九、線程池的幾種實現方式

  1. newCachedThreadPool建立一個可緩存線程池,若是線程池長度超過處理須要,可靈活回收空閒線程,若無可回收,則新建線程。
  2. newFixedThreadPool 建立一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。
  3. newScheduledThreadPool 建立一個定長線程池,支持定時及週期性任務執行。
  4. newSingleThreadExecutor 建立一個單線程化的線程池,它只會用惟一的工做線程來執行任務,保證全部任務按照指定順序(FIFO, LIFO, 優先級)執行。

十、線程的生命週期,狀態是如何轉移的

生命週期新建(New)、就緒(Runnable)、運行(Running)、阻塞(Blocked)和死亡(Dead)5種狀態

狀態轉移

1. 新建狀態,當程序使用new關鍵字建立了一個線程以後,該線程就處於新建狀態,此時僅由JVM爲其分配內存,並初始化其成員變量的值

2. 就緒狀態,當線程對象調用了start()方法以後,該線程處於就緒狀態。Java虛擬機會爲其建立方法調用棧和程序計數器,等待調度運行

3. 運行狀態,若是處於就緒狀態的線程得到了CPU,開始執行run()方法的線程執行體,則該線程處於運行狀態

4. 阻塞狀態,當處於運行狀態的線程失去所佔用資源以後,便進入阻塞狀態

5. 在線程的生命週期當中,線程的各類狀態的轉換過程

參考:https://www.cnblogs.com/sunddenly/p/4106562.html    

 

 

 

     另有線程相關面試題:https://mp.weixin.qq.com/s/rW0H5oez1IfMOtaPBwWGoA(可能和上面的有重複)

相關文章
相關標籤/搜索