Java多線程html
整理轉載自:java
http://blog.csdn.net/evankaka編程
http://www.javashuo.com/article/p-ccdmtvsq-cz.html安全
http://www.runoob.com/java/java-multithreading.html多線程
目錄dom
1、 線程和進程 1 ide
2、 繼承Thread類和實現Runnable實現多線程 2函數
1. 繼承Thread類 2this
2. 如何使用synchronized同步解決多線程共享數據同步問題 12
3. 討論synchronized用到不一樣地方對代碼產生的影響: 12
5. synchronized, wait, notify結合:典型場景生產者消費者問題 14
首先咱們先了解下在操做系統中進程和線程的區別:
Java想要實現多線程,有兩種方法,一種是繼承Thread類,另外一種是實現Runnable接口。
class Thread1 extends Thread{ public Thread1(){ //重寫類的構造方法 } public void run(){ //重寫父類run方法 } } public class Main {
public static void main(String[] args) { Thread1 mTh1=new Thread1("A"); Thread1 mTh2=new Thread1("B"); mTh1.start(); mTh2.start();
}
} |
輸出: A運行 : 0 B運行 : 0 A運行 : 1 A運行 : 2 A運行 : 3 A運行 : 4 B運行 : 1 B運行 : 2 B運行 : 3 B運行 : 4 再運行一下: A運行 : 0 B運行 : 0 B運行 : 1 B運行 : 2 B運行 : 3 B運行 : 4 A運行 : 1 A運行 : 2 A運行 : 3 A運行 : 4 |
程序啓動運行main()的時候,Java虛擬機會啓動一個進程,主線程main在main(0函數調用時候被建立,隨着調用Main類的兩個對象的start()方法,另外兩個線程也被啓動,這樣,整個應用程序就在多線程下運行。
線程對象只會調用start()方法,不會調用run()方法,run()方法的執行是由操做系統決定的。
注意:start()方法調用後並非當即執行多線程代碼,而是使得該線程變爲可運行態(Runnable),何時運行是由操做系統決定的。
從程序運行的結果能夠發現,多線程程序是亂序執行的,只有亂序執行的代碼纔有必要設計爲多線程。
Thread.sleep()方法調用的目的是不讓當前線程獨自霸佔該進程所獲取的CPU資源,以留出必定時間給其餘線程執行的機會。
實際上,全部的多線程代碼的執行順序都是不肯定的,每次執行的結果都是隨機的。
可是start方法重複調用的話,會出現java.lang.IllegalThreadStateException異常。
採用實現Runnable接口也是建立多線程的一種方法
class Thread2 implements Runnable{ private String name;
public Thread2(String name) { this.name=name; }
@Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(name + "運行 : " + i); try { Thread.sleep((int) Math.random() * 10); } catch (InterruptedException e) { e.printStackTrace(); } }
}
} public class Main {
public static void main(String[] args) { new Thread(new Thread2("C")).start(); new Thread(new Thread2("D")).start(); }
} |
輸出:
C運行 : 0 D運行 : 0 D運行 : 1 C運行 : 1 D運行 : 2 C運行 : 2 D運行 : 3 C運行 : 3 D運行 : 4 C運行 : 4 |
Thread2類經過實現Runnable接口,使得該類有了多線程類的特徵。run()方法是多線程程序的一個約定。全部的多線程代碼都在run方法裏面。Thread類實際上也是實現了Runnable接口的類。
在啓動的多線程的時候,須要先經過Thread類的構造方法Thread(Runnable target) 構造出對象,而後調用Thread對象的start()方法來運行多線程代碼。
實際上全部的多線程代碼都是經過運行Thread的start()方法來運行的。所以,無論是擴展Thread類仍是實現Runnable接口來實現多線程,最終仍是經過Thread的對象的API來控制線程的,熟悉Thread類的API是進行多線程編程的基礎。
若是一個類繼承Thread,則不適合資源共享。可是若是實現了Runable接口的話,則很容易的實現資源共享。
總結:
實現Runnable接口比繼承Thread類所具備的優點:
1):適合多個相同的程序代碼的線程去處理同一個資源
2):能夠避免java中的單繼承的限制
3):增長程序的健壯性,代碼能夠被多個線程共享,代碼和數據獨立
4):線程池只能放入實現Runable或callable類線程,不能直接放入繼承Thread的類
提醒一下你們:main方法其實也是一個線程。在java中因此的線程都是同時啓動的,至於何時,哪一個先執行,徹底看誰先獲得CPU的資源。
在java中,每次程序運行至少啓動2個線程。一個是main線程,一個是垃圾收集線程。由於每當使用java命令執行一個類的時候,實際上都會啓動一個JVM,每個jVM實習在就是在操做系統中啓動了一個進程。
使用new關鍵字和Thread類或其子類創建一個線程對象後,該線程對象就處於新建狀態。它保持這個狀態直到程序start()這個線程。
當線程調用了start()方法以後,該線程就進入就緒狀態。就緒狀態的線程處於就緒隊列中,要等待JVM裏線程調度器的調度。
若是就緒狀態的線程獲取CPU資源,就能夠執行run(),此時線程便處於運行狀態。處於運行狀態的線程最爲複雜,它能夠變爲阻塞狀態、就緒狀態和死亡狀態。
若是一個線程執行了sleep()休眠、suspend()掛起等方法,失去所佔用資源以後,該線程就從運行狀態進入阻塞狀態。在睡眠時間已到或得到設備資源後能夠從新進入就緒狀態。能夠分爲如下三種:
Sleep()不會釋放持有的鎖,只會超時使得線程終止。
每個 Java 線程都有一個優先級,這樣有助於操做系統肯定線程的調度順序。Java 線程的優先級是一個整數,其取值範圍是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。
線程能夠具備的最高優先級,取值爲10。
線程能夠具備的最低優先級,取值爲1。
默認狀況下,每個線程都會分配一個優先級 NORM_PRIORITY(5)。
具備較高優先級的線程對程序更重要,而且應該在低優先級的線程以前分配處理器資源。可是,線程優先級不能保證線程執行的順序,並且很是依賴於平臺。
Thread.sleep(long millis)方法,使線程轉到阻塞狀態。millis參數設定睡眠的時間,以毫秒爲單位。當睡眠結束後,就轉爲就緒(Runnable)狀態
線程等待:Object類中的wait()方法,致使當前的線程等待,直到其餘線程調用此對象的 notify() 方法或 notifyAll() 喚醒方法。
線程讓步:Thread.yield() 方法,暫停當前正在執行的線程對象,把執行機會讓給相同或者更高優先級的線程。
線程加入:join()方法,等待其餘線程終止。在當前線程中調用另外一個線程的join()方法,則當前線程轉入阻塞狀態,直到另外一個進程運行結束,當前線程再由阻塞轉爲就緒狀態。
在指定的毫秒數內讓當前正在執行的線程休眠(暫停運行)
指等待t線程終止。該線程是指的主線程等待子線程的終止。有時主線程須要等待子線程執行完成以後再結束,這個時候就要用到join()方法了。
暫停當前正在執行的線程對象,並執行其餘線程。
yield()應該作的是讓當前運行線程回到可運行狀態,以容許具備相同優先級的其餘線程得到運行機會。
yield()不會致使線程轉到等待/睡眠/阻塞狀態
在進一步闡述以前,咱們須要明確幾點:
A.不管synchronized關鍵字加在方法上仍是對象上,它取得的鎖都是對象,而不是把一段代碼或函數看成鎖――並且同步方法極可能還會被其餘線程的對象訪問。
B.每一個對象只有一個鎖(lock)與之相關聯。
C.實現同步是要很大的系統開銷做爲代價的,甚至可能形成死鎖,因此儘可能避免無謂的同步控制。
Public synchronized void methodAAA() { //…. } |
這也就是同步方法,那這時synchronized鎖定的是哪一個對象呢?它鎖定的是調用這個同步方法對象。也就是說,當一個對象P1在不一樣的線程中執行這個同步方法時,它們之間會造成互斥,達到同步的效果。可是這個對象所屬的Class所產生的另外一對象P2卻能夠任意調用這個被加了synchronized關鍵字的方法。
public void method3(SomeObject so){ synchronized(so){ //….. } } |
這時,鎖就是so這個對象,誰拿到這個鎖誰就能夠運行它所控制的那段代碼。當有一個明確的對象做爲鎖時,就能夠這樣寫程序。
Class Foo { public synchronized static void methodAAA() { //…. } public void methodBBB() { synchronized(Foo.class) // class literal(類名稱字面常量) } } |
代碼中的methodBBB()方法是把class literal做爲鎖的狀況,它和同步的static函數產生的效果是同樣的,取得的鎖很特別,是當前調用這個方法的對象所屬的類(Class,而再也不是由這個Class產生的某個具體對象了)。
public synchronized void consume() { if(this.product <= MIN_PRODUCT) { try { wait(); System.out.println("缺貨,稍候再取"); } catch (InterruptedException e) { e.printStackTrace(); } return; } System.out.println("消費者取走了第" + this.product + "個產品."); this.product--; notifyAll(); //通知等待去的生產者能夠生產產品了 } |
main memory(主存)、working memory(線程棧),在處理數據時,線程會把值從主存load到本地棧,完成操做後再save回去(volatile關鍵詞的做用:每次針對該變量的操做都激發一次load and save)。