併發
進程&線程
進程
進程的本質是一個正在執行的程序,程序運行時系統會建立一個進程,而且給每一個進程分配獨立的內存地址空間保證每一個進程地址不會相互干擾。進程是具備必定獨立功能的程序、它是系統進行資源分配和調度的一個獨立單位,重點在系統調度和單獨的單位。同時,在 CPU 對進程作時間片的切換時,保證進程切換過程當中仍然要從進程切換以前運行的位置處開始執行。因此進程一般還會包括程序計數器、堆棧指針。 有了進程之後,可讓操做系統從宏觀層面實現多應用併發。而併發的實現是經過 CPU 時間片不端切換執行的。對於單核 CPU 來講,在任意一個時刻只會有一個進程在被CPU 調度。單CPU進行進程調度的時候,須要讀取上下文+執行程序+保存上下文,即進程切換。安全
線程
線程是進程的一個實體,是CPU調度和分派的基本單位,他是比進程更小的能獨立運行的基本單位,線程本身基本上不擁有系統資源。在運行時,只是暫用一些計數器、寄存器和棧 。輕量級的進程, 線程能夠合理利用多核心CPU資源,提升程序的吞吐量。多線程
線程的存在感
- 在多核 CPU 中,利用多線程能夠實現真正意義上的並行執行
- 在一個應用進程中,會存在多個同時執行的任務,若是其中一個任務被阻塞,將會引發不依賴該任務的任務也被阻塞。經過對不一樣任務建立不一樣的線程去處理,能夠提高程序處理的實時性
- 線程能夠認爲是輕量級的進程,因此線程的建立、銷燬比進程更快
- 在進程中的不一樣線程爲了使用CPU核心,則會進行線程切換,可是因爲共享了程序執行環境,這個線程切換比進程切換開銷少了不少。在這裏依然是併發,惟一核心同時刻只能執行一個線程。
總結:併發
- 單CPU中進程只能是併發,多CPU計算機中進程能夠並行。
- 單CPU單核中線程只能併發,單CPU多核中線程能夠並行。
- 不管是併發仍是並行,使用者來看,看到的是多進程,多線程。
- 一個線程只能屬於一個進程,而一個進程能夠有多個線程,但至少有一個線程(一般說的主線程)。
- 資源分配給進程,同一進程的全部線程共享該進程的全部資源。
- 線程在執行過程當中,須要協做同步。不一樣進程的線程間要利用消息通訊的辦法實現同步。
- 處理機分給線程,即真正在處理機上運行的是線程。
- 線程是指進程內的一個執行單元,也是進程內的可調度實體。
從三個角度來剖析兩者之間的區別
- 調度:線程做爲調度和分配的基本單位,進程做爲擁有資源的基本單位。
- 併發性:不只進程之間能夠併發執行,同一個進程的多個線程之間也能夠併發執行。
- 擁有資源:進程是擁有資源的一個獨立單位,線程不擁有系統資源,但能夠訪問隸屬於進程的資源。
線程的應用
如何應用多線程
在 Java 中,有多種方式來實現多線程。繼承 Thread 類、實現 Runnable 接口、使用 ExecutorService、Callable、Future 實現帶返回結果的多線程。jvm
- 繼承 Thread 類建立線程
Thread 類本質上是實現了 Runnable 接口的一個實例,表明一個線程的實例。啓動線程的惟一方法就是經過 Thread類的 start()實例方法。start()方法是一個 native 方法,它會啓動一個新線程,並執行 run()方法。這種方式實現多線程很簡單,經過本身的類直接 extend Thread,並複寫 run()方法,就能夠啓動新線程並執行本身定義的 run()方法。
public class MyThread extends Thread { public void run() { System.out.println("MyThread.run()"); } } MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); myThread1.start(); myThread2.start();
- 實現 Runnable 接口建立線程
若是本身的類已經 extends 另外一個類,就沒法直接 extends Thread,此時,能夠實現一個 Runnable 接口ide
public class MyThread extends OtherClass implements Runnable { public void run() { System.out.println("MyThread.run()"); } }
3.實現 Callable 接口經過 FutureTask 包裝器來建立 Thread 線程
當須要讓一步執行的線程在執行完成之後,提供一個返回值給到當前的主線程,主線程須要依賴這個值進行後續的邏輯處理,那麼這個時候,就須要用到 帶返回值的線程了。 Java 中提供了這樣的實現方式操作系統
public class CallableDemo implements Callable<String> { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService= Executors.newFixedThreadPool(1); CallableDemo callableDemo=new CallableDemo(); Future<String> future=executorService.submit(callableDemo); System.out.println(future.get()); executorService.shutdown(); } @Override public String call() throws Exception { int a=1; int b=2; System.out.println(a+b); return "執行結果:"+(a+b); } }
線程生命週期
線程一共有 6 種狀態(NEW、RUNNABLE、BLOCKED、WAITING、TIME_WAITING、TERMINATED)
NEW:初始狀態,線程被構建,可是尚未調用 start 方法
RUNNABLED:運行狀態,JAVA 線程把操做系統中的就緒(READY)和運行(RUNNING)兩種狀態統一稱爲「運行中」
BLOCKED:阻塞狀態,表示線程進入等待狀態,也就是線程,由於某種緣由放棄了 CPU 使用權,阻塞也分爲幾種狀況
➢ 等待阻塞:運行的線程執行 wait 方法,jvm 會把當前線程放入到等待隊列
➢ 同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被其餘線程鎖佔用了,那麼 jvm 會把當前的線程放入到鎖池中
➢ 其餘阻塞:運行的線程執行 Thread.sleep 或者 t.join 方法,或者發出了 I/O 請求時,JVM 會把當前線程設置爲阻塞狀態,當 sleep 結束、join 線程終止、io 處理完畢則線程恢復
TIME_WAITING:超時等待狀態,超時之後自動返回
TERMINATED:終止狀態,表示當前線程執行完畢
線程
線程的終止
interrupt 方法
當其餘線程經過調用當前線程的 interrupt 方法,表示向當前線程打個招呼,告訴他能夠中斷線程的執行了,至於何時中斷,取決於當前線程本身。 這種經過標識位或者中斷操做的方式可以使線程在終止時有機會去清理資源,而不是武斷地將線程中止,所以這種終止線程的作法顯得更加安全和優雅。 線程經過檢查資深是否被中斷來進行相應,能夠經過isInterrupted()來判斷是否被中斷。
thread.interrupt()方法實際就是設置一個 interrupted 狀態標識爲 true、而且經過ParkEvent 的 unpark 方法來喚醒線程。指針
- 對於 synchronized 阻塞的線程,被喚醒之後會繼續嘗試獲取鎖,若是失敗仍然可能被 park
- 在調用 ParkEvent 的 park 方法以前,會先判斷線程的中斷狀態,若是爲 true,會清除當前線程的中斷標識
- Object.wait 、 Thread.sleep 、 Thread.join 會 拋 出InterruptedException
Thread.interrupted
經過 interrupt,設置了一個標識告訴線程可 以 終 止 了 , 線 程 中 還 提 供 了 靜 態 方 法Thread.interrupted()對設置中斷標識的線程復位。外面的線程調用 thread.interrupt 來設置中斷標識,而在線程裏面,又經過 Thread.interrupted 把線程的標識又進行了復位。code
其餘的線程復位
除了經過 Thread.interrupted 方法對線程中斷標識進行復位之外,還 有 一 種 被 動 復 位 的 場 景,就 是對拋出InterruptedException 異 常 的 方 法,在 InterruptedException拋出以前,JVM 會先把線程的中斷標識位清除,而後纔會拋出InterruptedException,這個時候若是調用 isInterrupted方法,將會返回 false.對象
爲何要復位
Thread.interrupted()是屬於當前線程的,是當前線程對外界中斷信號的一個響應,表示本身已經獲得了中斷信號,但不會馬上中斷本身,具體何時中斷由本身決定,讓外界知道在自身中斷前,他的中斷狀態仍然是 false,這就是復位的緣由。
InterruptedException
Object.wait、Thread.sleep 和 Thread.join都會拋出InterruptedException。阻塞的方法釋放會取決於一些外部的事件,可是阻塞方法可能由於等不到外部的觸發事件而致使沒法終止,因此它容許一個線程請求本身來中止它正在作的事情。當一個方法拋出 InterruptedException 時,它是在告訴調用者若是執行該方法的線程被中斷,它會嘗試中止正在作的事情而且經過拋出 InterruptedException表示提早返回。因此,這個異常的意思是表示一個阻塞被其餘線程中斷了。 而後 ,因爲線程調 用了interrupt() 中斷方法,那 麼Object.wait、Thread.sleep等被阻塞的線程被喚醒之後會經過 is_interrupted 方法判斷中斷標識的狀態變化,若是發現中斷標識爲 true,則先清除中斷標識(中斷標識置爲false),而後拋出InterruptedException。須要注意的是,InterruptedException異常的拋出並不意味着線程必須終止,而是提醒當前線程有中斷的操做發生,至於接下來怎麼處理取決於線程自己,好比
- 直接捕獲異常不作任何處理
- 將異常往外拋出
- 中止當前線程,並打印異常信息
--------知識淺薄,望君勿噴。