Java線程:創建與啟動
SCJP5學習筆記
一、定義線程
1、擴展java.lang.Thread類。
此類中有個run()方法,應該注意其用法:
public void run()
-
如果該線程是使用獨立的
Runnable
運行對象構造的,則調用該Runnable
對象的run
方法;否則,該方法不執行任何操作並返回。 -
Thread
的子類應該重寫該方法。
2、實現java.lang.Runnable接口。
void
run()
-
使用實現接口
Runnable
的對象創建一個線程時,啟動該線程將導致在獨立執行的線程中調用對象的run
方法。 -
方法
run
的常規協定是,它可能執行任何所需的操作。
二、實例化線程
1、如果是擴展java.lang.Thread類的線程,則直接new即可。
2、如果是實現了java.lang.Runnable接口的類,則用Thread的構造方法:
Thread(Runnable target)
Thread(Runnable target, String name)
Thread(ThreadGroup group, Runnable target)
Thread(ThreadGroup group, Runnable target, String name)
Thread(ThreadGroup group, Runnable target, String name, long stackSize)
Thread(Runnable target, String name)
Thread(ThreadGroup group, Runnable target)
Thread(ThreadGroup group, Runnable target, String name)
Thread(ThreadGroup group, Runnable target, String name, long stackSize)
三、啟動線程
在線程的Thread對象上調用start()方法,而不是run()或者別的方法。
在調用start()方法之前:線程處於新狀態中,新狀態指有一個Thread對象,但還沒有一個真正的線程。
在調用start()方法之后:發生了一系列復雜的事情
啟動新的執行線程(具有新的調用棧);
該線程從新狀態轉移到可運行狀態;
當該線程獲得機會執行時,其目標run()方法將運行。
注意:對Java來說,run()方法沒有任何特別之處。像main()方法一樣,它只是新線程知道調用的方法名稱(和簽名)。因此,在Runnable上或者Thread上調用run方法是合法的。但並不啟動新的線程。
四、例子
1、實現Runnable接口的多線程例子
package com.dashu.thread; public class DoSomething implements Runnable { private String name; public DoSomething(String name) { this.name = name; } public void run() { // TODO Auto-generated method stub for (int i = 0; i < 10; i++) { for (long k = 0; k < 100000000; k++) ; System.out.println(name + ": " + i); } } }
package com.dashu.thread; public class TestThread { public static void main(String[] args) { DoSomething do1 = new DoSomething("烏龜"); DoSomething do2 = new DoSomething("王八"); Thread t1 = new Thread(do1); Thread t2 = new Thread(do2); t1.start(); t2.start(); } }
2、擴展Thread類實現的多線程例子
package com.dashu.thread; public class TestThread extends Thread{ public TestThread(String name) { super(name); } public void run() { for(int i = 0;i<5;i++){ for(long k= 0; k <100000000;k++); System.out.println(this.getName()+" :"+i); } } public static void main(String[] args) { Thread t1 = new TestThread("王八"); Thread t2 = new TestThread("烏龜"); t1.start(); t2.start(); } }
執行的結果和方法一差不多。
對於上面的多線程程序代碼來說,輸出的結果是不確定的。其中的一條語句for(long k= 0; k <100000000;k++);是用來模擬一個非常耗時的操作的。
五、一些常見問題
1、線程的名字,一個運行中的線程總是有名字的,名字有兩個來源,一個是虛擬機自己給的名字,一個是你自己的定的名字。在沒有指定線程名字的情況下,虛擬機總會為線程指定名字,並且主線程的名字總是mian,非主線程的名字不確定。
2、線程都可以設置名字,也可以獲取線程的名字,連主線程也不例外。
3、獲取當前線程的對象的方法是:Thread.currentThread();
4、在上面的代碼中,只能保證:每個線程都將啟動,每個線程都將運行直到完成。一系列線程以某種順序啟動並不意味着將按該順序執行。對於任何一組啟動的線程來說,調度程序不能保證其執行次序,持續時間也無法保證。
5、當線程目標run()方法結束時該線程完成。
6、一旦線程啟動,它就永遠不能再重新啟動。只有一個新的線程可以被啟動,並且只能一次。一個可運行的線程或死線程可以被重新啟動。
7、線程的調度是JVM的一部分,在一個CPU的機器上上,實際上一次只能運行一個線程。一次只有一個線程棧執行。JVM線程調度程序決定實際運行哪個處於可運行狀態的線程。
眾多可運行線程中的某一個會被選中做為當前線程。可運行線程被選擇運行的順序是沒有保障的。
8、盡管通常采用隊列形式,但這是沒有保障的。隊列形式是指當一個線程完成“一輪”時,它移到可運行隊列的尾部等待,直到它最終排隊到該隊列的前端為止,它才能被再次選中。事實上,我們把它稱為可運行池而不是一個可運行隊列,目的是幫助認識線程並不都是以某種有保障的順序排列唱呢個一個隊列的事實。
9、盡管我們沒有無法控制線程調度程序,但可以通過別的方式來影響線程調度的方式。