Java併發編程想必你們都不陌生,它是實現高併發/高流量的基礎,今天咱們就來一塊兒學習這方面的內容。java
簡單來講,進程就是程序的一次執行過程,它是系統進行資源分配和調度的基本單位。線程與進程相似,可是線程是更小的執行單位。一個進程在執行過程當中可產生多個線程,正由於如此,線程也被稱爲輕量級的進程。線程和進程在於進程大可能是獨立工做的,而各線程則不必定,同一進程下的線程極有可能相互干擾。學習了以上內容,回答第一個問題想必就不在話下了。編程
Talk is cheap,shou me the code. 接下來就讓咱們經過建立線程,來開啓咱們的併發之旅吧。多線程
建立線程一般有三種方式,分別爲繼承 Thread 類(重寫run方法),實現Runable接口以及使用FutureTask方式。併發
public class MyThread extends Thread { @Override public void run() { System.out.println(Thread.currentThread()+"I am a new thread "); } public static void main(String[] args) { MyThread thread1=new MyThread(); MyThread thread2=new MyThread(); thread1.start(); thread2.start(); } }
如上述的代碼所示,咱們繼承Thread而且重寫了run方法,並開啓了兩個線程。ide
該代碼執行結果以下:函數
Thread[Thread-0,5,main]I am a new thread
Thread[Thread-1,5,main]I am a new thread高併發
值得一提的是,建立完Thread對象後線程並無啓動,直到調用了start方法才真正啓動。學習
那麼問題就來了,咱們調用 start() 方法時會執行 run() 方法,爲何咱們不能直接調用 run() 方法?spa
咱們new一個Thread,線程就進入了新建狀態,而調用了start方法以後它纔會進入就緒狀態,等待CPU時間片的分配。就緒狀態+時間片=線程真正運行,它會自動執行run方法。若是直接調用run方法,main線程會把它當作一個普通的方法去執行,這不是多線程工做(仍然在主線程執行)。線程
public class MyThread implements Runnable { @Override public void run() { System.out.println(Thread.currentThread() + "I am a new thread "); } } public static void main(String[] args) { MyThread thread1 = new MyThread(); MyThread thread2 = new MyThread(); new Thread(thread1).start(); new Thread(thread2).start(); }
此代碼執行結果和代碼一類似,因爲Java只支持單繼承,若是使用繼承Thread的方式就不能再繼承其餘的類,而Runnable沒有這個限制,可是他們有一個共同的缺點,任務沒有返回值,咱們接着來看最後一種方式。
public class CallerTask implements Callable<String> { @Override public String call() throws Exception { return Thread.currentThread() + " hello world"; } } public static void main(String[] args) { FutureTask<String> futureTask1 = new FutureTask<>(new CallerTask()); FutureTask<String> futureTask2 = new FutureTask<>(new CallerTask()); new Thread(futureTask1).start(); new Thread(futureTask2).start(); try { String result1 = futureTask1.get(); String result2 = futureTask2.get(); System.out.println(result1); System.out.println(result2); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }
該代碼執行結果以下:
Thread[Thread-0,5,main] hello world
Thread[Thread-1,5,main] hello world
如上述代碼所示,咱們實現了Callable接口的call()方法,在main函數中建立了兩個FurtureTask對象,而且將其啓動。經過futureTask.get()方法拿到了咱們的返回值。
當一個線程調用一個共享變量的wait方法的時候,該線程會被阻塞掛起。若是wait() 方法事先沒有獲取鎖,那麼它就會拋出異常。
若是發生瞭如下幾種狀況,wait狀態將會被打破。
當前線程調用共享對象的wait() 方法的時候,當前線程只會釋放當前共享對象的鎖,當前線程持有的其餘共享對象的鎖不會被釋放。
一個線程調用共享對象的notify() 方法以後,會喚醒一個處於等待狀態的線程,它並不會肯定的喚醒某個線程,而是由JVM決定。被喚醒的線程並不能繼續執行,由於它的鎖狀態已經發生了改變,必須去競爭獲取鎖,才能進入就緒狀態。
notifyAll()方法會喚醒該共享變量上全部處於等待狀態的線程,可是值得注意的是,它只會喚醒調用這個方法以前被阻塞的線程。以後阻塞的它就無能爲力了。
調用該方法會暫時讓出指定時間的執行權,也就是在此期間不參與CPU的調度,可是該線程所擁有的監視器資源,畢如鎖仍是持有不讓出的。到了指定時間以後,他就會處於就緒狀態,等待獲取CPU,從新進入運行狀態。
接下類咱們談談sleep()和wait()的聯繫和區別
本篇文章主要介紹了線程的建立以及併發過程的經常使用方法,但願認真讀完的你有所收穫。