一、基礎知識
學習線程之前先了解一下有關的基礎知識
(1)進程
一般可以在同一時間內執行多個程序的操作系統都有進程的概念。一個進程就是一個執行中的程序, 而每一個進程都有自己獨立的一塊內存空間、一組系統資源。在進程的概念中,每一個進程的內部數 據和狀態都是完全獨立的。
(2)線程
線程與進程相似,是一段完成某個特定功能的代碼,是程序中單個順序控制的流程,但與進程不同的 是,同類的多個線程是共享一塊內存空間和一組系統資源。所以系統在各個線程之間切換時,開銷要 比進程小的多,正因如此,線程被稱爲輕量級進程。一個進程中可以包含多個線程。
(3)主線程
Java程序至少會有一個線程,這就是主線程,程序啓動後是由JVM創建主線程,程序結束時由JVM停 止主線程。主線程它負責管理子線程,即子線程的啓動、掛起、停止等等操作。圖所示是進程、 主線程和子線程的關係,其中主線程負責管理子線程,即子線程的啓動、掛起、停止等操作。
獲取主線程示例代碼如下:
public class HelloWorld {
public static void main(String[] args) {
// 獲取主線程
Thread mainThread = Thread.currentThread();
System.out.println("主線程名"+mainThread.getName());
}
}
運行結果:
主線程名main
Thread.currentThread()獲得當前線程,由於在main()方法中當前線程就是主線程,
Thread是Java線程類,位於java.lang包中
getName()方法獲得線程的名字,主線程名是 main,由JVM分配。
二、創建子線程
Java中創建一個子線程涉及到:
java.lang.Thread類和java.lang.Runnable接口。
Thread是線程類,創建一 個Thread對象就會產生一個新的線程。
而線程執行的程序代碼是在實現Runnable接口對象的run()方法 中編寫的,實現Runnable接口對象是線程執行對象。
線程執行對象實現Runnable接口的run()方法,run()方法是線程執行的入口,該線程要執行程序代碼都 在此編寫的,run()方法稱爲線程體。
注意:主線程中執行入口是main(String[] args)方法,這裏可以控制程序的流程,管理其他的子線 程等。
子線程執行入口是線程執行對象(實現Runnable接口對象)的run()方法,在這個方法可以 編寫子線程相關處理代碼。
詳細瞭解java.lang.Thread類和java.lang.Runnable接口見Java官方API文檔
http://www.matools.com/api/java8
有以下幾種方式創建子線程
(1)通過實現Runnable接口
創建步驟:
1、通過實現Runnable接口創建線程執行類
2、通過重寫Runnable中的run方法,編寫線程執行代碼
3、創建線程Thread對象,將線程執行對象傳遞給它
4、開始線程
創建線程Thread對象時,可以將線程執行對象傳遞給它,這需要是使用Thread類如下兩個構造方法:
- Thread(Runnable target, String name):target是線程執行對象,實現Runnable接口。name爲線程指 定一個名字。
- Thread(Runnable target):target是線程執行對象,實現Runnable接口。線程名字是由JVM分配 的。
實現Runnable接口的線程執行對象Runner代碼如下:
public class Runner implements Runnable {
// 編寫執行線程的代碼,重寫Runnable接口中的run方法,run()方法是線程體,在該方法中編寫你自己的線程處理代碼。
@Override
public void run() {
for (int i=0;i<10;i++){
// 打印線程次數和線程名
System.out.printf("第%d次執行--%s\n",i,Thread.currentThread().getName());
try {
// 隨機生成休眠時間
long sleepTime = (long)(1000*Math.random());
// 線程休眠
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 線程執行結束
System.out.println("執行完成"+Thread.currentThread().getName());
}
}
測試代碼:
public class HelloWorld {
public static void main(String[] args) {
// 創建一個線程t1,參數是線程執行對象Runner
Thread t1 = new Thread(new Runner());
t1.start();
// 創建一個線程t2,參數是線程執行對象Runner
Thread t2 = new Thread(new Runner(),"MyThread");
t2.start();
}
}
Thread.currentThread()可以獲得當前線程對象,getName()是Thread類的實例方法,可以獲得線程的名。
Thread.sleep(sleepTime)是休眠當前線程,sleep是靜態方法:
- static void sleep(long millis):在指定的毫秒數內讓當前正在執行的線程休眠。
- static void sleep(long millis, int nanos) 在指定的毫秒數加指定的納秒數內讓當前正在執行的線程 休眠。
線程創建完成還 需要調用start()方法才能執行,start()方法一旦調用線程進入可以執行狀態, 可以執行狀態下的線程等待CPU調度執行,CPU調用後線程進行執行狀態,運行run()方法。
運行結果如下:
第0次執行--Thread-0
第0次執行--MyThread
第1次執行--Thread-0
第1次執行--MyThread
第2次執行--MyThread
第2次執行--Thread-0
第3次執行--MyThread
第4次執行--MyThread
第3次執行--Thread-0
第4次執行--Thread-0
第5次執行--MyThread
第6次執行--MyThread
第5次執行--Thread-0
第6次執行--Thread-0
第7次執行--Thread-0
第7次執行--MyThread
第8次執行--Thread-0
第8次執行--MyThread
第9次執行--MyThread
第9次執行--Thread-0
執行完成Thread-0
執行完成MyThread
提示: 仔細分析一下運行結果,會發現兩個線程是交錯運行的,感覺就像是兩個線程在同時運行。但是實際上一臺PC通常就只有一顆CPU,在某個時刻只能是一個線程在運行,而Java語言在 設計時就充分考慮到線程的併發調度執行。對於程序員來說,在編程時要注意給每個線程執行的 時間和機會,主要是通過讓線程休眠的辦法(調用sleep()方法)來讓當前線程暫停執行,然後由 其他線程來爭奪執行的機會。如果上面的程序中沒有用到sleep()方法,則就是第一個線程先執行 完畢,然後第二個線程再執行完畢。所以用活sleep()方法是多線程編程的關鍵。
(2)通過繼承Thread線程類
事實上Thread類也實現了Runnable接口,所以Thread類也可以作爲線程執行對象,這需要繼承Thread類,覆蓋run()方法。
創建步驟:
1、通過繼承Thread線程類創建線程執行類
2、定義構造方法,通過super調用父類Thread構造方法
這兩個Thread類 構造方法:
- Thread(String name):name爲線程指定一個名字。
- Thread():線程名字是JVM分配的。
3、通過重寫Thread中的run方法,編寫線程執行代碼
4、創建線程執行對象,將參數傳遞給它
5、開始線程
代碼如下:
//線程執行對象
public class MyThread extends Thread {
// 調用了一個構造方法,通過super調用父類的構造方法
public MyThread(){
super();
}
public MyThread(String name){
super(name);
}
// 編寫執行線程代碼
// 重寫Threa的的run方法
@Override
public void run() {
for (int i = 0; i < 10; i++) {
// 打印線程次數和線程名
System.out.printf("第%d次執行--%s\n", i,getName());
try {
// 隨機生成休眠時間
long sleepTime = (long) (1000 * Math.random());
// 線程休眠
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 線程執行結束
System.out.println("執行完成" + getName());
}
}
測試代碼:
public class HelloWorld {
public static void main(String[] args) {
// 創建線程t1
Thread t1 = new MyThread();
// 開始線程t1
t1.start();
// 創建線程t1
Thread t2 = new MyThread("mythread");
// 開始線程t1
t2.start();
}
}
由於Java只支持單重繼承,繼承Thread類的方式不能再繼承其他父類。當開發一些圖形界 面的應用時,需要一個類既是一個窗口(繼承JFrame)又是一個線程體,那麼只能採用實現 Runnable接口方式。
(3)使用匿名內部類和Lambda表達式實現線程體
如果線程體使用的地方不是很多,可以不用單獨定義一個類。可以使用匿名內部類或Lambda表達式直接實現Runnable接口。Runnable中只有一個方法是函數式接口,可以使用Lambda表達式。
代碼如下:
//使用匿名內部類和Lambda表達式實現線程體
public class HelloWorld {
public static void main(String[] args) {
// 創建線程t1,參數是實現Runnable接口的匿名內部類
Thread t1 = new Thread(new Runnable() {
// 編寫執行線程代碼
@Override
public void run() {
for (int i=0;i<10;i++){
// 打印線程次數和線程名
System.out.printf("第%d次執行--%s\n",i,Thread.currentThread().getName());
try {
// 隨機生成休眠時間
long sleepTime = (long)(1000*Math.random());
// 線程休眠
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 線程執行結束
System.out.println("執行完成"+Thread.currentThread().getName());
}
});
// 開始線程t1
// 創建線程t2,參數是實現Runnable接口的Lambda表達式
Thread t2 = new Thread(() -> { //重寫run方法
for (int i=0;i<10;i++){
// 打印線程次數和線程名
System.out.printf("第%d次執行--%s\n",i,Thread.currentThread().getName());
try {
// 隨機生成休眠時間
long sleepTime = (long)(1000*Math.random());
// 線程休眠
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 線程執行結束
System.out.println("執行完成"+Thread.currentThread().getName());
},"MyThread");
t2.start();
}
}
匿名內部類和Lambda表達式不需要定義一個線程類文件,使用起來很方便。特別是 Lambda表達式使代碼變得非常簡潔。
以上內容僅供參考學習,如有侵權請聯繫我刪除!
如果這篇文章對您有幫助,左下角的大拇指就是對博主最大的鼓勵。
您的鼓勵就是博主最大的動力!