併發編程可使咱們將程序劃分爲多個分離的,獨立運行的任務。經過多線程機制,這些獨立任務都將由執行線程來驅動。在使用線程時,CPU將輪流給每一個任務分配佔用時間,每一個任務都以爲本身在佔用CPU,但實際上CPU時間是劃分爲片斷分配給了全部任務。java
定義任務編程
繼承Thread類多線程
咱們能夠繼承Thread類,並重寫run方法。併發
public class SimpleThread extends Thread { @Override public void run(){ for(int i = 0; i < 5; i++){ System.out.println(getName() + i); } } public static void main(String[] args) { SimpleThread thread = new SimpleThread(); thread.start(); System.out.println(Thread.currentThread().getName()); } }
實現Runnable接口異步
固然,咱們還能夠實現Runnable接口,並實現run接口,而後提交給Thread實例。ide
public class Task implements Runnable{ public void run(){ for(int i = 0; i < 5; i++){ System.out.println(Thread.currentThread().getName() + i); } } public static void main(String[] args) { Thread thread = new Thread(new Task()); thread.start(); System.out.println(Thread.currentThread().getName()); } }
Callable與Futurespa
咱們知道run方法是沒有返回值的,也就意味着任務完成後沒法獲取結果,因此咱們須要Callable接口來幫助咱們返回任務結果,它和Runnable接口很類似,因此咱們也須要實現Callable接口的call方法。而Future接口則用來獲取異步計算結果的,咱們對執行結果獲取,取消,或者判斷是否完成。可是Callable接口並無繼承Runnable,因此並不能直接提交給Thread實例,因此咱們還須要FutureTask類,它同時實現了Runnable接口和Callable接口,咱們能夠用FutureTask包裝Callable對象,再提交給Thread實例。操作系統
import java.util.concurrent.*; public class TaskWithResult implements Callable<Integer> { public Integer call(){ int total = 0; for(int i = 0; i < 100; i++){ total += i; } return total; } public static void main(String[] args) throws InterruptedException, ExecutionException{ RunnableFuture<Integer> task = new FutureTask<Integer>(new TaskWithResult()); Thread thread = new Thread(task); thread.start(); while (!task.isDone()){ System.out.println(task.get().toString()); } } }
後臺線程線程
後臺線程,也叫守護線程,是指程序在運行的時候在後臺提供一種通用服務的線程,而且這種線程並不屬於程序中不可或缺的部分。因此,當全部非後臺線程結束時,後臺線程也會被結束,無論後臺線程是否完成。並且,後臺線程的子線程也是後臺線程。code
public class Daemon implements Runnable { @Override public void run() { for(int i = 0; i < 10; i++){ try{ Thread.sleep(1000); System.out.println(i); }catch (InterruptedException e){ System.out.println(e.getMessage()); } } } public static void main(String[] args) throws InterruptedException{ Thread thread = new Thread(new Daemon()); thread.setDaemon(true); thread.start(); Thread.sleep(1000 * 5); System.out.println(Thread.currentThread().getName()); } }
這裏要注意setDaemon方法必需要在start以前調用,才能將其設置爲後臺線程。
線程的生命週期
新建(new)
當線程被建立時,它只會短暫地處於這種狀態。此時它已經分配了必須的系統資源,並執行了初始化。此刻線程已經有資格得到CPU時間了,以後調度器將把這個線程轉變爲可運行狀態或阻塞狀態。
就緒(Runnable)
在這種狀態下,只要調度器把時間片分配給線程,線程就能夠運行。也就是說,在任意時刻,線程能夠運行也能夠不運行。只要調度器能分配時間片給線程,它就能夠運行,這不一樣於死亡和阻塞狀態。
阻塞(Blocked)
線程可以運行,但有某個條件阻止它的運行。當線程處於阻塞狀態時,調度器將忽略線程,不會分配給線程任何CPU時間。直到線程從新進入了就緒狀態,它纔有可能執行操做。
一個任務進入阻塞狀態,可能有如下緣由:
(1)經過調用sleep()使任務進入休眠狀態,在這種狀況下,任務在指定時間內不會運行。
(2)經過調用wait()使線程掛起。直到線程獲得了notify()或notifyAll()消息,線程纔會進入就緒狀態。
(3)任務在等待某個輸入/輸出完成。
(4)任務試圖在某個對象上調用其同步控制方法,可是對象鎖不可用,由於另一個任務已經獲取了這個鎖。
死亡(Dead)
處於死亡或終止狀態的線程將再也不是可調度的,而且不再會獲得CPU時間,它的任務已結束,或再也不是可運行的。任務死亡的一般方式是從run()方法返回,可是任務的線程還能夠被中斷。
線程控制
線程優先級
線程的優先級將該線程的重要性傳遞給調度器。儘管CPU處理現有線程集的順序是不肯定的,可是調度器將傾向於讓優先級最高的線程先執行。固然,這並不意味着優先權較低的線程將得不到執行(優先級不會致使死鎖)。咱們能夠用getPriority()來讀取現有線程的優先級,經過setPriority()來修改它。儘管JDK有10個優先級,可是它與多數操做系統都不能映射的很好。因此設置優先級時,通常使用MAX_PRIORITY、NORM_PRIORITY、MIN_PRIORITY三種級別。
休眠(Sleep)
在線程執行的過程當中調用sleep方法讓其休眠(也就是進入阻塞狀態)。sleep會拋出InterruptedException。
public class Sleep { public static void main(String[] args) { System.out.println("休眠開始"); try{ Thread.sleep(1000 * 2); }catch (InterruptedException e){ System.out.println(e.getMessage()); } System.out.println("休眠結束"); } }
讓步(Yield)
若是知道已經完成了run方法的循環的一次迭代過程當中所需的工做,就能夠給線程調度機制一個暗示:你的工做已經作的差很少了,可讓別的線程使用CPU了,這個
暗示將經過調用yeild做出,固然,這只是一種建議,沒有任何機制保證它將會採納。當線程切換出去後,只有優先級與當前線程相同,或優先級比當前線程更高的處於就緒的線程纔會得到執行機會,所以徹底有可能線程轉入就緒後,調度器又將其調度出來從新執行。
Join
join能夠一個讓線程等待另外一個線程執行完成,調用線程將被阻塞,直到被join
的線程執行完成。
public class Task implements Runnable{ public void run(){ for(int i = 0; i < 5; i++){ System.out.println(Thread.currentThread().getName() + i); } try{ Thread.sleep(1000); }catch (InterruptedException e){ System.out.println(e.getMessage()); } } public static void main(String[] args) throws Exception{ Thread thread = new Thread(new Task()); thread.start(); thread.join(); System.out.println(Thread.currentThread().getName()); } }