什麼是併發編程?
併發編程就是一個程序運行時(進程)有多個線程在運行,它出現的原因就是爲了提高程序的運行性能。其應用場景就是對於一個任務可以拆分爲多個並行的方向去運行。舉個例子:車站賣票時一般都是多個窗口通過程序同時在賣票,這其實就是一種併發編程最常見的場景。雖然這種"併發"賣票的方式提高了賣票的效率,但是由此而來的幾個問題也隨之而來。一趟列車車票總數是一定的,多個窗口通過應用程序賣票時要保證一張票只能被一個窗口賣,另外需要保證的是所賣出的票的總數不能超過一個值,這些問題都是在程序中需要考慮的。
併發編程的一些基本概念
進程與線程:
這裏推薦一下阮一峯老師的一篇文章:進程與線程的一個簡單解釋
併發與並行
現代的計算機基本都是多核計算機,即有多個CPU。這就代表了計算機在同一時刻可以運行多個程序,這被稱爲並行模式。而在單核計算機時代,計算機同一時刻只能運行一個程序,但是當程序運行到需要做IO操作的時候,此時CPU是處於一個等待狀態,這就是一種浪費,爲了在程序執行長時間的IO操作或其他阻塞操作時又可以利用CPU,計算機科學家把CPU的運行做了一個分時處理,即CPU的運行是由n個CPU運行的單位時間組成的,它在一個時間段內可以運行多個程序,因爲在程序運行到執行IO操作時,可以把CPU的執行時間片分配給其他程序。這樣子從宏觀看,CPU好像在運行多個程序,這樣一種模式即併發模式。
Java中創建線程的四種方式
繼承Thread類
public class ThreadDemo extends Thread {
//重寫run方法
@Override
public void run(){
...
}
}
public class Test{
public static void main(String[] args){
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
}
}
實現Runable接口
public class ThreadDemo implements Runable{
@Override
public void run(){
...
}
}
public class Test{
public static void main(String[] args){
Thread thread = new Thread(new ThreadDemo());
thread.start();
}
}
利用Callable和FutureTask
這種方式可以獲取線程的執行結果
public class ThreadDemo implements Callable<V>{
@Override
public V call() throws Exception{
...
}
}
public class Test{
public static void main(String[] args) {
FutureTask<Object> task = new FutureTask<>(new ThreadDemo());
Thread thread = new ThreadDemo(task);
thread.start();
//獲取線程執行結果
Object result = task.get();
}
}
利用線程池技術
這個後面單獨一篇博客講解
Java中線程的六種狀態
新建態:當一個線程被創建完畢但是並未調用其start方法時,它就處於新建態,此時它並不會被分配CPU的運行時間片。
運行態:當調用了線程的start方法,線程就處於運行態。Java中線程的運行態其實還可以分爲就緒態和運行中。所謂的就緒態就是線程並未被分配CPU運行時間片,處於一個等待分配的狀態,而運行中是指線程被分配了CPU運行時間片,正在運行。當調用的線程Thread類的**public static void yield()**方法的時候,其實線程就是從運行中變爲就緒態,線程讓出CPU。
等待狀態:它描述的是線程處於暫停的狀態,它是一種無限期等待,也就是說它如果想繼續運行必須得等其他線程喚醒它纔可以。通過調用wait()、join()、park()方法可以讓線程進入等待狀態,而通過調用noitify()、notifyAll()、unpark()等方法可以喚醒線程,這些方法詳細使用另外的文章會介紹。
超時等待:它與上面的等待狀態的區別是它在一定時間內沒有被喚醒它會自動從超時等待狀態進入運行態。
阻塞態:它是指線程去執行被synchronized修飾的方法或代碼塊時無法獲取synchronized鎖而被阻塞的一種狀態,直到線程獲取到鎖才進入運行態。
終止:終止狀態是指線程執行完畢。