在早起的裸機時代,計算機很是地昂貴,並且也沒有操做系統的概念,計算機從頭至尾只能執行一個程序。若是程序在執行一個耗時的操做,那麼在這個過程當中,計算機就有大量的資源閒置在那裏,這是很是浪費的。html
而這個時候,操做系統的概念被提出了。在操做系統的控制下,一個計算機能夠執行不少的程序。計算機的資源由操做系統進行分配,程序之間得到計算機資源並執行各自的任務,相互獨立。操做系統的出現使得計算機資源的利用率大大增長。java
你也能夠將操做系統理解爲,運行程序的程序。類比AI是一種產生算法的算法。算法
操做系統能夠執行多個程序,每一個程序在計算機中便是一個「進程」。進程的執行是並行的,實際上這裏的「並行」並不徹底是物理意義上的並行。操做系統會給每一個進程分配必定的執行時間,而且CPU在這些進程之間快速地切換執行,從而近似地達到了一種並行執行的效果。固然,現代CPU也從早期的單核發展爲4核、8核,能夠從物理上同時執行。即便是單核,也可以控制多個執行流達到物理上併發的目的。編程
操做系統使得程序得以併發執行,但隨着需求的增長,咱們但願每一個程序也可以併發多個任務。因此,線程的概念就應運而生了。線程是依賴於進程的,共享進程的計算機資源。一個進程能夠擁有一到多個線程。線程的並行執行和進程的理念是同樣的,共享進程的時間片,快速地執行切換。api
Java編程語言爲幫助開發者開發多線程的應用程序設計了一套簡單的語義,你能夠利用Java的內置支持來構建多線程程序,充分利用CPU資源,提升程序的響應速度。多線程
下圖,摘錄自菜鳥教程:http://www.runoob.com/java/java-multithreading.html併發
這個圖相信你們都很熟悉,它是表達了線程的生命週期,包含了幾個狀態,以下:異步
1)建立:當咱們在程序中new出來一個線程實例對象的時候,線程便處於建立狀態;編程語言
2)就緒:當咱們調用了start()方法,表示該線程即將等待JVM的native方法去調用咱們的線程,也就是就緒狀態;ide
3)運行:若是JVM調用了咱們的線程,線程實例執行中,它就處於運行狀態;
4)阻塞:運行過程當中可能出現sleep、wait或者其它IO操做等,這個時候當前的線程就處於阻塞狀態,讓出CPU的資源,而不是繼續佔用。
5)死亡:若是線程正常執行完畢,手動退出或者意外結束,那麼線程就進入了死亡狀態,該線程佔用的資源也會被自動回收。
Java顯示建立線程有兩種實現:
public class ThreadDemo extends Thread { @Override public void run() { System.out.println("執行了" + Thread.currentThread().getName()); } public static void main(String[] args) { new ThreadDemo().start(); System.out.println("主線程執行了" + Thread.currentThread().getName()); } }
事實上,Thread類實現了Runnable接口,而咱們重寫了Runnable的run方法,當調用start()方法的時候,該線程會調用native方法,從而JVM會去異步執行線程實例的run方法。
public class RunnableDemo implements Runnable { @Override public void run() { System.out.println("線程執行" + Thread.currentThread().getName()); } public static void main(String[] args) { new Thread(new RunnableDemo()).start(); System.out.println("主線程執行" + Thread.currentThread().getName()); } }
實現Runnable接口的方式,也須要將Runnable做爲Thread實例的構造入參,而後經過start()將線程轉換爲就緒狀態。若是你直接調用run方法是不會異步運行的。
以上兩種實現,其實都是基於Runnable接口和Thread類,只是寫法不一樣。
yield執行的時候暫停當前線程執行,讓出CPU資源,去執行其它線程
public class YieldDemo { private static volatile boolean isReady = false; public static void main(String[] args) throws InterruptedException { new Thread(() -> { System.out.println(Thread.currentThread().getName() + "讓主線程執行"); while (!isReady) { Thread.yield(); } System.out.println(Thread.currentThread().getName() + "執行完畢"); }).start(); Thread.sleep(5000); isReady = true; System.out.println(Thread.currentThread().getName() + "執行完畢"); } }
join的做用是讓將某個線程插入到當前線程的執行位置,並等待該線程執行完畢之後再執行當前線程。
public class JoinDemo { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { System.out.println(Thread.currentThread().getName() + "開始執行"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "執行完畢"); }); thread.start(); System.out.println(Thread.currentThread().getName() + "等待子線程執行完畢"); thread.join(); System.out.println(Thread.currentThread().getName() + "執行完畢"); } }
notify和wait使用比較特殊,須要得到同步鎖資源
notify和wait方法是Object對象的方法,任何對象都會有。它的做用是什麼呢?咱們舉一個場景示例:
1)存在兩個線程A和B;
2)咱們但願A執行完畢之後通知B去執行,而B在A通知以前一直等待
代碼示例以下:
public class WaitDemo { private static Object lock = new Object(); public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 3; i++) { new Thread(() -> { // 要先得到同步鎖 synchronized (lock) { try { System.out.println(Thread.currentThread().getName() + " waiting"); // 等待notify lock.wait(); System.out.println(Thread.currentThread().getName() + " end waiting"); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } System.out.println("sleep"); Thread.sleep(5000); // 要先得到同步鎖 synchronized (lock) { System.out.println("notify"); // 執行notify lock.notifyAll(); System.out.println("finished"); } } }
這裏隨意創建了一個lock對象做爲鎖對象,子線程拿到這個鎖之後執行等待。而主線程拿到鎖之後,發送通知,執行結果以下:
sleep Thread-0 waiting Thread-1 waiting Thread-2 waiting notify finished Thread-2 end waiting Thread-1 end waiting Thread-0 end waiting
注意:若是notifyAll()在wait()以前執行了,那麼子線程將無線等待下去,你能夠給wait設置超時時間
更多API示例,參考JDK文檔:http://tool.oschina.net/uploads/apidocs/jdk-zh/java/lang/Thread.html