1, Java中編寫多執行緒程式和其他的程式語言相比容易很多。主要透過Runnable介面和Thread類別來實現。
publicclassSimpleRunnable implements Runnable{ private String message; publicstaticvoid main(String[] args) { SimpleRunnabler1 = new SimpleRunnable("Hello"); Threadt1 = new Thread(r1); t1.start(); for(;;){ System.out.println("Bye-bye"); } } public SimpleRunnable(String message){ this.message = message; } @Override publicvoid run() { for(;;){ System.out.println(message); } } }
以上,透過繼承於Runable介面實作run()來實作多線程,透過Thread來實作虛擬CPU,讓執行緒r1在獨立的執行緒中運行。事實上,該線程和主線程並不是完全的獨立運行的線程,而是r1線程和主線程輪番切換運行,只是切換的運行速度特別快,看起來像是兩個線程獨立運行。
2, 執行緒的切換,根據不同的作業系統而有所不同:
一種是排隊機制,如果一個執行緒搶得了CPU,直到該執行緒運行結束,或被動停止才推出。
一種是透過優先權模式,優先權最高的執行緒優先權搶到CPU。
一種是時間片,每個執行緒擁有公平的時間片,當一個執行緒運行完時間片後退出,其他的執行緒擁有公平的機會搶佔CPU。
Java的執行緒採用基於優先權和時間片綜合的方式,具體實現根據運行的作業系統而有所不同。比如說當一個線程時間片運行完後退出並將優先權降一級,然後排序優先權比較高的線程搶佔CPU,如果運行的過程中來了一個優先級更高的線程,則會搶佔當前的CPU。
3, 線程運行狀態:
線程停止(Pause)包括,sleep(),wait(),suspend(),I/O blocking
線程重啟(Run)包括,sleep time-out,notify() ,resume(),I/O finished
線程終止,stop()
線程等待,join()
線程主動讓出CPU,yield()
以上suspend(),resume(),stop()都已過時,不再建議使用。
4, 其中sleep()函數是static的,是屬於類別的函數,而不是物件。這個函數將使呼叫的執行緒暫停一定時間,而不是某一個其他的執行緒停止。如下
publicstaticvoidmain(String[] args)throwsException { SimpleRunnabler1 = new SimpleRunnable("Hello"); Threadt1 = newThread(r1); t1.start(); for(inti = 0; i < 10; i++){ System.out.println("Bye-bye"); } t1.sleep(10000); t1.stop(); }
以上程式碼是讓主執行緒停止10秒鐘,而不是讓t1停止10秒鐘。另外sleep()讓執行緒暫停的時間是個模糊值,以上理論將使主執行緒暫停10秒鐘,但由於執行緒切換的不確定性,導致主執行緒並不是精確的暫停10秒鐘。
5, 另外一個static的函數是yield(),他表示目前正在運行的線程主動讓出CPU,該函數也是僅能作用於當前線程類似於以上sleep()函數。
6, 程式設計是為了追求高效率的結果,採用多執行緒。但是多執行緒運行是很難預測的,因此我們會採取一定的方法,讓多執行緒的運行結果盡量可預測。
class Thread1 extends Thread{ @Override publicvoid run(){ try{ System.out.println("thread1 start"); sleep(10000); // <3> System.out.println("thread1 finish");//<4> }catch(Exception e){ } } } class Thread2 extends Thread{ @Override publicvoid run(){ try{ System.out.println("thread2 start"); suspend(); // <2> System.out.println("thread2 finish");// <7> }catch(Exception e){ } } } publicclassThreadTest { publicstaticvoid main(String[] args) { Threadt1 = new Thread1(); Thread2t2 = new Thread2(); t1.start(); t2.start(); try{ System.out.println("s1"); t1.join(); // <1> System.out.println("s2"); // <5> t2.resume(); //<6> System.out.println("s3"); // <8> t2.join(); // <9> }catch(Exception e){ } System.out.println("s4"); // <10> } }
以上運行其中一次的運行結果為:
thread1 start
thread2 start
s1
thread1 finish
s2
但是並不是每次都得到這樣的結果。但是也有一部分是可預測的。哪些是可預測的?哪些是不可預測的? (1) 前三行一定會佔據前三行,由於的程式碼使主執行緒停止,等待執行緒t1結束,而t1執行緒在處使執行緒暫停10秒鐘,同時處程式碼是線程t2處於掛起狀態,知道有程式碼喚醒該線程,否則該線程就會一直處於阻塞狀態。但是前三行的輸出順序是無法預測的。 (2) 第四行一定會在第四行,當t1休眠結束後,首先運行處程式碼(3) 第五行一定會在第五行,當t1執行結束後,主執行緒取得CPU開始運行程式碼(4) 第六行和第七行的位置可能會互換,當程式碼喚醒執行緒t2後從開始運行,同時主執行緒繼續運行(5) 最後一行一定會在最後一行。在暫停主線程,等待t2運行結束,運行輸出最後一行,主線程結束退出。