標籤:
1. 代碼執行個體
這裡我們做一個完整的例子來說明線程產生的方式不同而產生的線程的區別:
package com.example.ThreadTestDemo;public class ThreadTest extends Thread { public static void main(String[] args) throws Exception{ for(int i=0;i<10;i++){ Thread t = new MyThread(); t.start(); } Thread.sleep(10000); //讓上面的線程運行完成 R r = new R(); for(int i=0;i<10;i++){ Thread t = new Thread(r); t.start(); } }}class MyThread extends Thread{ public int x = 0; public void run(){ System.out.println(++x); } } class R implements Runnable{ private int x = 0; public void run(){ System.out.println(++x); } }
輸出結果為:1 1 1 1 1 1 1 1 1 1
1 2 5 4 3 8 10 7 6 9
上面10個線程對象產生的10個線程運行時列印了10次1。下面10個線程對象產生的10個線程運行時列印了1到10。我們把下面的10個線程稱為同一執行個體(Runnable執行個體)的多個線程。
2. 線程對象的幾個重要方法
1. start方法
一個線程對象產生後,如果要產生一個執行的線程,就一定要調用它的start()方法。在介紹這個方法時不得不同時說明run方法。其實線程對象的run方法完全是一個介面回調方法,它是你這個線程對象要完成的具體邏輯。簡單說,你要做什麼你就在run方法中完成。而如何做,什麼時候做就不需要你控制了,你只要調用start方法,JVM就會管理這個線程對象讓它產生一個線程並註冊到線程管理系統中。
從表面上看,start()方法調用了run()方法,事實上並沒有直接調用run方法。在JDK1.5以前start()方法是本地方法,它如何最終調用run方法已經不是JAVA程式員所能瞭解的。而在JDK1.5中,原來的那個本地start方法被start0()代替,另一個純JAVA的start()中調用本地方法start0(),而在start()方法中做了一個驗證,就是對一個全域變數(物件變數)started做檢驗,如果為true,則start()拋出異常,不會調用本地方法start0(),否則,現將該變數設為true,然後調用start0()。
從中我們可以看到這個為了控制一個線程對象只能運行成功一次start()方法。這是因為線程的運行要擷取當前環境,包括安全,父線程的許可權,優先順序等條件,如果一個線程可以運行多次,那麼定義一個static的線程在一個環境中擷取相應許可權和優先順序,運行完成後它在另一個環境中利用原來的許可權和優先順序等屬性在當前環境中運行,這樣就造成無法預知的結果。簡單說,讓一個線程對象只能成功運行一次,是基於對線程管理的需要。
start()方法最本質的功能是從CPU中申請另一個線程空間來執行run方法中的代碼,它和當前線程是兩條線,在相對獨立的線程空間運行。也就是說,如果你直接調用線程對象的run方法,當然也會執行,但那是在當前線程中執行,run方法執行完成後繼續執行下面的代碼。而調用start()方法後,run方法的代碼會和當前線程並發(單CPU)或並行(多CPU)執行。
所以請記住一句話:調用線程對象的run方法不會產生一個新的線程,雖然可以達到相同的執行結果,但執行過程和執行效率不同。
2. sleep,join,yield方法
先簡單說一下這些方法的作用和調用原則。
[轉]java多線程例子