線程基礎知識

併發編程三要素是什麼?在 Java 程序中怎麼保證多線程的運行安全?

併發編程三要素(線程的安全性問題體如今):算法

原子性:原子,即一個不可再被分割的顆粒。原子性指的是一個或多個操做要麼所有執行成功要麼所有執行失敗。編程

可見性:一個線程對共享變量的修改,另外一個線程可以馬上看到。(synchronized,volatile)緩存

有序性:程序執行的順序按照代碼的前後順序執行。(處理器可能會對指令進行重排序)安全

出現線程安全問題的緣由:多線程

線程切換帶來的原子性問題併發

緩存致使的可見性問題app

編譯優化帶來的有序性問題異步

解決辦法:函數

JDK Atomic開頭的原子類、synchronized、LOCK,能夠解決原子性問題
synchronized、volatile、LOCK,能夠解決可見性問題
Happens-Before 規則能夠解決有序性問題工具

並行和併發有什麼區別?

  • 併發:多個任務在同一個 CPU 核上,按細分的時間片輪流(交替)執行,從邏輯上來看那些任務是同時執行。
  • 並行:單位時間內,多個處理器或多核處理器同時處理多個任務,是真正意義上的「同時進行」。

什麼是線程和進程?以及根本區別?

進程

一個在內存中運行的應用程序。每一個進程都有本身獨立的一塊內存空間,一個進程能夠有多個線程,好比在Windows系統中,一個運行的xx.exe就是一個進程。

線程

進程中的一個執行任務(控制單元),負責當前進程中程序的執行。一個進程至少有一個線程,一個進程能夠運行多個線程,多個線程可共享數據。
根本區別:

進程是操做系統資源分配的基本單位,而線程是處理器任務調度和執行的基本單位。

什麼是上下文切換?

歸納來講就是:當前任務在執行完 CPU 時間片切換到另外一個任務以前會先保存本身的狀態,以便下次再切換回這個任務時,能夠再加載這個任務的狀態。任務從保存到再加載的過程就是一次上下文切換。

建立線程有哪幾種方式?

建立線程有四種方式:

  • 繼承 Thread 類;
  • 實現 Runnable 接口;
  • 實現 Callable 接口;
  • 使用 Executors 工具類建立線程池

說一下 runnable 和 callable 有什麼區別?

相同點

都是接口

均可以編寫多線程程序

都採用Thread.start()啓動線程

主要區別

Runnable 接口 run 方法無返回值;Callable 接口 call 方法有返回值,是個泛型,和Future、FutureTask配合能夠用來獲取異步執行的結果
Runnable 接口 run 方法只能拋出運行時異常,且沒法捕獲處理;Callable 接口 call 方法容許拋出異常,能夠獲取異常信息
注:Callalbe接口支持返回執行結果,須要調用FutureTask.get()獲得,此方法會阻塞主進程的繼續往下執行,若是不調用不會阻塞。

線程的 run()和 start()有什麼區別?

start() 方法用於啓動線程,run() 方法用於執行線程的運行時代碼。run() 能夠重複調用,而 start() 只能調用一次。

start()方法來啓動一個線程,真正實現了多線程運行。調用start()方法無需等待run方法體代碼執行完畢,能夠直接繼續執行其餘的代碼; 此時線程是處於就緒狀態,並無運行。 而後經過此Thread類調用方法run()來完成其運行狀態, run()方法運行結束, 此線程終止。而後CPU再調度其它線程。

run()方法是在本線程裏的,只是線程裏的一個函數,而不是多線程的。 若是直接調用run(),其實就至關因而調用了一個普通函數而已,直接待用run()方法必須等待run()方法執行完畢才能執行下面的代碼,因此執行路徑仍是隻有一條,根本就沒有線程的特徵,因此在多線程執行時要使用start()方法而不是run()方法。

爲何咱們調用 start() 方法時會執行 run() 方法,爲何咱們不能直接調用 run() 方法?

new 一個 Thread,線程進入了新建狀態。調用 start() 方法,會啓動一個線程並使線程進入了就緒狀態,當分配到時間片後就能夠開始運行了。 start() 會執行線程的相應準備工做,而後自動執行 run() 方法的內容,這是真正的多線程工做。

而直接執行 run() 方法,會把 run 方法當成一個 main 線程下的普通方法去執行,並不會在某個線程中執行它,因此這並非多線程工做。

總結: 調用 start 方法方可啓動線程並使線程進入就緒狀態,而 run 方法只是 thread 的一個普通方法調用,仍是在主線程裏執行。

什麼是 Callable 和 Future?

Callable 接口相似於 Runnable,從名字就能夠看出來了,可是 Runnable 不會返回結果,而且沒法拋出返回結果的異常,而 Callable 功能更強大一些,被線程執行後,能夠返回值,這個返回值能夠被 Future 拿到,也就是說,Future 能夠拿到異步執行任務的返回值。

Future 接口表示異步任務,是一個可能尚未完成的異步任務的結果。因此說 Callable用於產生結果,Future 用於獲取結果。

什麼是 FutureTask

FutureTask 表示一個異步運算的任務。FutureTask 裏面能夠傳入一個 Callable 的具體實現類,能夠對這個異步運算的任務的結果進行等待獲取、判斷是否已經完成、取消任務等操做。只有當運算完成的時候結果才能取回,若是運算還沒有完成 get 方法將會阻塞。一個 FutureTask 對象能夠對調用了 Callable 和 Runnable 的對象進行包裝,因爲 FutureTask 也是Runnable 接口的實現類,因此 FutureTask 也能夠放入線程池中。

說說線程的生命週期及五種基本狀態?

1.新建(new):新建立了一個線程對象。

2.可運行(runnable):線程對象建立後,當調用線程對象的 start()方法,該線程處於就緒狀態,等待被線程調度選中,獲取cpu的使用權。

3.運行(running):可運行狀態(runnable)的線程得到了cpu時間片(timeslice),執行程序代碼。注:就緒狀態是進入到運行狀態的惟一入口,也就是說,線程要想進入運行狀態執行,首先必須處於就緒狀態中;

4.阻塞(block):處於運行狀態中的線程因爲某種緣由,暫時放棄對 CPU的使用權,中止執行,此時進入阻塞狀態,直到其進入到就緒狀態,才 有機會再次被 CPU 調用以進入到運行狀態。
5.死亡(dead):線程run()、main()方法執行結束,或者因異常退出了run()方法,則該線程結束生命週期。死亡的線程不可再次復生。

Java 中用到的線程調度算法是什麼?

有兩種調度模型:分時調度模型和搶佔式調度模型。

分時調度模型是指讓全部的線程輪流得到 cpu 的使用權,而且平均分配每一個線程佔用的 CPU 的時間片這個也比較好理解。

Java虛擬機採用搶佔式調度模型,是指優先讓可運行池中優先級高的線程佔用CPU,若是可運行池中的線程優先級相同,那麼就隨機選擇一個線程,使其佔用CPU。處於運行狀態的線程會一直運行,直至它不得不放棄 CPU。

請說出與線程同步以及線程調度相關的方法。

(1) wait():使一個線程處於等待(阻塞)狀態,而且釋放所持有的對象的鎖;

(2)sleep():使一個正在運行的線程處於睡眠狀態,是一個靜態方法,調用此方法要處理 InterruptedException 異常;

(3)notify():喚醒一個處於等待狀態的線程,固然在調用此方法的時候,並不能確切的喚醒某一個等待狀態的線程,而是由 JVM 肯定喚醒哪一個線程,並且與優先級無關;

(4)notityAll():喚醒全部處於等待狀態的線程,該方法並非將對象的鎖給全部線程,而是讓它們競爭,只有得到鎖的線程才能進入就緒狀態;

sleep() 和 wait() 有什麼區別?

二者均可以暫停線程的執行

類的不一樣:sleep() 是 Thread線程類的靜態方法,wait() 是 Object類的方法。
是否釋放鎖:sleep() 不釋放鎖;wait() 釋放鎖。
用途不一樣:Wait 一般被用於線程間交互/通訊,sleep 一般被用於暫停執行。
用法不一樣:wait() 方法被調用後,線程不會自動甦醒,須要別的線程調用同一個對象上的 notify() 或者 notifyAll() 方法。sleep() 方法執行完成後,線程會自動甦醒。或者可使用wait(long timeout)超時後線程會自動甦醒。

Thread 類中的 yield 方法有什麼做用?

使當前線程從執行狀態(運行狀態)變爲可執行態(就緒狀態)。

當前線程到了就緒狀態,那麼接下來哪一個線程會從就緒狀態變成執行狀態呢?多是當前線程,也多是其餘線程,看系統的分配了。

線程的 sleep()方法和 yield()方法有什麼區別?

(1) sleep()方法給其餘線程運行機會時不考慮線程的優先級,所以會給低優先級的線程以運行的機會;yield()方法只會給相同優先級或更高優先級的線程以運行的機會;

(2) 線程執行 sleep()方法後轉入阻塞(blocked)狀態,而執行 yield()方法後轉入就緒(ready)狀態;

(3)sleep()方法聲明拋出 InterruptedException,而 yield()方法沒有聲明任何異常;

(4)sleep()方法比 yield()方法(跟操做系統 CPU 調度相關)具備更好的可移植性,一般不建議使用yield()方法來控制併發線程的執行。

相關文章
相關標籤/搜索