1. 程式人生 > 其它 >java 多執行緒 thread insert_Java多執行緒2:Thread中的例項方法

java 多執行緒 thread insert_Java多執行緒2:Thread中的例項方法

技術標籤:java 多執行緒 thread insert

Thread類中的方法呼叫方式:

學習Thread類中的方法是學習多執行緒的第一步。在學習多執行緒之前特別提出一點,呼叫Thread中的方法的時候,線上程類中,有兩種方式,一定要理解這兩種方式的區別:

1、this.XXX()

這種呼叫方式表示的執行緒是執行緒例項本身

2、Thread.currentThread.XXX()或Thread.XXX()

上面兩種寫法是一樣的意思。這種呼叫方式表示的執行緒是正在執行Thread.currentThread.XXX()所在程式碼塊的執行緒

當然,這麼說,肯定有人不理解兩者之間的差別。沒有關係,之後會講清楚,尤其是在講Thread建構函式這塊。講解後,再回過頭來看上面2點,會加深理解。

Thread類中的例項方法

從Thread類中的例項方法和類方法的角度講解Thread中的方法,這種區分的角度也有助於理解多執行緒中的方法。例項方法,只和例項執行緒(也就是new出來的執行緒)本身掛鉤,和當前執行的是哪個執行緒無關。看下Thread類中的例項方法:

1、start()

start()方法的作用講得直白點就是通知"執行緒規劃器",此執行緒可以運行了,正在等待CPU呼叫執行緒物件得run()方法,產生一個非同步執行的效果。通過start()方法產生得到結論,先看下程式碼:

public class MyThread02 extends Thread{ public void run() { try { for (int i = 0; i < 3; i++) { Thread.sleep((int)(Math.random() * 1000)); System.out.println("run = " + Thread.currentThread().getName()); } } catch (InterruptedException e) { e.printStackTrace(); } }}
public static void main(String[] args){ MyThread02 mt = new MyThread02(); mt.start();  try { for (int i = 0; i < 3; i++) { Thread.sleep((int)(Math.random() * 1000)); System.out.println("run = " + Thread.currentThread().getName()); } } catch (InterruptedException e) { e.printStackTrace(); }}

看下執行結果:

run = Thread-0run = mainrun = mainrun = mainrun = Thread-0run = Thread-0

結果表明了:CPU執行哪個執行緒的程式碼具有不確定性。再看另外一個例子:

public class MyThread03 extends Thread{ public void run() { System.out.println(Thread.currentThread().getName()); }}
public static void main(String[] args){ MyThread03 mt0 = new MyThread03(); MyThread03 mt1 = new MyThread03(); MyThread03 mt2 = new MyThread03();  mt0.start(); mt1.start(); mt2.start();}

看下執行結果:

Thread-1Thread-2Thread-0

儘管啟動執行緒是按照mt0、mt1、mt2,但是實際的啟動順序卻是Thread-1、Thread-2、Thread-0。這個例子說明了:呼叫start()方法的順序不代表執行緒啟動的順序,執行緒啟動順序具有不確定性

2、run()

執行緒開始執行,虛擬機器呼叫的是執行緒run()方法中的內容。稍微改一下之前的例子看一下:

public static void main(String[] args){ MyThread02 mt = new MyThread02(); mt.run();  try { for (int i = 0; i < 3; i++) { Thread.sleep((int)(Math.random() * 1000)); System.out.println("run = " + Thread.currentThread().getName()); } } catch (InterruptedException e) { e.printStackTrace(); }}

MyThread02的程式碼不變,看下執行結果:

run = mainrun = mainrun = mainrun = mainrun = mainrun = main

看到列印了6次的"run = main",說明如果只有run()沒有start(),Thread例項run()方法裡面的內容是沒有任何非同步效果的,全部被main函式執行。換句話說,只有run()而不呼叫start()啟動執行緒是沒有任何意義的。

3、isAlive()

測試執行緒是否處於活動狀態,只要執行緒啟動且沒有終止,方法返回的就是true。看一下例子:

public class MyThread06 extends Thread{ public void run() { System.out.println("run = " + this.isAlive()); }}
public static void main(String[] args) throws Exception{ MyThread06 mt = new MyThread06(); System.out.println("begin == " + mt.isAlive()); mt.start(); Thread.sleep(100); System.out.println("end == " + mt.isAlive());}

看下執行結果:

begin == falserun = trueend == false

看到在start()之前,執行緒的isAlive是false,start()之後就是true了。main函式中加上Thread.sleep(100)的原因是為了確保Thread06的run()方法中的程式碼執行完,否則有可能end這裡打印出來的是true,有興趣可以自己試驗一下。

4、getId()

這個方法比較簡單,就不寫例子了。在一個Java應用中,有一個long型的全域性唯一的執行緒ID生成器threadSeqNumber,每new出來一個執行緒都會把這個自增一次,並賦予執行緒的tid屬性,這個是Thread自己做的,使用者無法執行一個執行緒的Id。

5、getName()

這個方法也比較簡單,也不寫例子了。我們new一個執行緒的時候,可以指定該執行緒的名字,也可以不指定。如果指定,那麼執行緒的名字就是我們自己指定的,getName()返回的也是開發者指定的執行緒的名字;如果不指定,那麼Thread中有一個int型全域性唯一的執行緒初始號生成器threadInitNum,Java先把threadInitNum自增,然後以"Thread-threadInitNum"的方式來命名新生成的執行緒

6、getPriority()和setPriority(int newPriority)

這兩個方法用於獲取和設定執行緒的優先順序,優先順序高的CPU得到的CPU資源比較多,設定優先順序有助於幫"執行緒規劃器"確定下一次選擇哪一個執行緒優先執行。換句話說,兩個在等待CPU的執行緒,優先順序高的執行緒越容易被CU選擇執行。下面來看一下例子,並得出幾個結論:

public class MyThread09_0 extends Thread{ public void run() { System.out.println("MyThread9_0 run priority = " +  this.getPriority()); }}
public class MyThread09_1 extends Thread{ public void run() { System.out.println("MyThread9_1 run priority = " +  this.getPriority()); MyThread09_0 thread = new MyThread09_0(); thread.start(); }}
public static void main(String[] args){ System.out.println("main thread begin, priority = " +  Thread.currentThread().getPriority()); System.out.println("main thread end, priority = " +  Thread.currentThread().getPriority()); MyThread09_1 thread = new MyThread09_1(); thread.start();}

看一下執行結果:

main thread begin, priority = 5main thread end, priority = 5MyThread9_1 run priority = 5MyThread9_0 run priority = 5

從這個例子我們得出結論:執行緒預設優先順序為5,如果不手動指定,那麼執行緒優先順序具有繼承性,比如執行緒A啟動執行緒B,那麼執行緒B的優先順序和執行緒A的優先順序相同。下面的例子演示了設定執行緒優先順序帶來的效果:

public class MyThread10_0 extends Thread{ public void run() { long beginTime = System.currentTimeMillis(); for (int j = 0; j < 100000; j++){} long endTime = System.currentTimeMillis(); System.out.println("◆◆◆◆◆ thread0 use time = " +  (endTime - beginTime)); }}
public class MyThread10_1 extends Thread{ public void run() { long beginTime = System.currentTimeMillis(); for (int j = 0; j < 100000; j++){} long endTime = System.currentTimeMillis(); System.out.println("◇◇◇◇◇ thread1 use time = " +  (endTime - beginTime)); }}
public static void main(String[] args){ for (int i = 0; i < 5; i++) { MyThread10_0 mt0 = new MyThread10_0(); mt0.setPriority(5); mt0.start(); MyThread10_1 mt1 = new MyThread10_1(); mt1.setPriority(4); mt1.start(); }}

看下執行結果:

◆◆◆◆◆ thread0 use time = 0◆◆◆◆◆ thread0 use time = 0◆◆◆◆◆ thread0 use time = 1◆◆◆◆◆ thread0 use time = 2◆◆◆◆◆ thread0 use time = 2◇◇◇◇◇ thread1 use time = 0◇◇◇◇◇ thread1 use time = 1◇◇◇◇◇ thread1 use time = 5◇◇◇◇◇ thread1 use time = 2◇◇◇◇◇ thread1 use time = 0

看到黑色菱形(執行緒優先順序高的)先列印完,再多試幾次也是一樣的。為了產生一個對比效果,把yMyThread10_0的優先順序設定為4,看下執行結果:

◆◆◆◆◆ thread0 use time = 0◇◇◇◇◇ thread1 use time = 1◇◇◇◇◇ thread1 use time = 1◆◆◆◆◆ thread0 use time = 0◇◇◇◇◇ thread1 use time = 0◆◆◆◆◆ thread0 use time = 1◆◆◆◆◆ thread0 use time = 1◇◇◇◇◇ thread1 use time = 2◇◇◇◇◇ thread1 use time = 1◆◆◆◆◆ thread0 use time = 0

看到,馬上差別就出來了。從這個例子我們得出結論:CPU會盡量將執行資源讓給優先順序比較高的執行緒

7、isDaeMon、setDaemon(boolean on)

講解兩個方法前,首先要知道理解一個概念。Java中有兩種執行緒,一種是使用者執行緒,一種是守護執行緒。守護執行緒是一種特殊的執行緒,它的作用是為其他執行緒的執行提供便利的服務,最典型的應用便是GC執行緒。如果程序中不存在非守護執行緒了,那麼守護執行緒自動銷燬,因為沒有存在的必要,為別人服務,結果服務的物件都沒了,當然就銷燬了。理解了這個概念後,看一下例子:

public class MyThread11 extends Thread{ private int i = 0;  public void run() { try { while (true) { i++; System.out.println("i = " + i); Thread.sleep(1000); } }  catch (InterruptedException e) { e.printStackTrace(); } }}
public static void main(String[] args) { try { MyThread11 mt = new MyThread11(); mt.setDaemon(true); mt.start(); Thread.sleep(5000); System.out.println("我離開thread物件再也不列印了,我停止了!"); } catch (InterruptedException e) { e.printStackTrace(); } }

看一下執行結果:

 1 i = 1 2 i = 2 3 i = 3 4 i = 4 5 i = 5 6 我離開thread物件再也不列印了,我停止了! 7 i = 6

要解釋一下。我們將MyThread11執行緒設定為守護執行緒,看到第6行的那句話,而i停在6不會再運行了。這說明,main執行緒運行了5秒多結束,而i每隔1秒累加一次,5秒後main執行緒執行完結束了,MyThread11作為守護執行緒,main函式都執行完了,自然也沒有存在的必要了,就自動銷燬了,因此也就沒有再往下列印數字。

關於守護執行緒,有一個細節注意下,setDaemon(true)必須線上程start()之前

8、interrupt()

這是一個有點誤導性的名字,實際上Thread類的interrupt()方法無法中斷執行緒。看一下例子:

public class TestMain12_0{ public static void main(String[] args) { try { MyThread12 mt = new MyThread12(); mt.start(); Thread.sleep(2000); mt.interrupt(); }  catch (InterruptedException e) { e.printStackTrace(); } }}
public void run(){ for (int i = 0; i < 500000; i++) { System.out.println("i = " + (i + 1)); }}

看下執行結果:

...i = 499995i = 499996i = 499997i = 499998i = 499999i = 500000

看結果還是列印到了50000。也就是說,儘管呼叫了interrupt()方法,但是執行緒並沒有停止。interrupt()方法的作用實際上是:線上程受到阻塞時丟擲一箇中斷訊號,這樣執行緒就得以退出阻塞狀態。換句話說,沒有被阻塞的執行緒,呼叫interrupt()方法是不起作用的。關於這個會在之後講中斷機制的時候,專門寫一篇文章講解。

9、isInterrupted()

測試執行緒是否已經中斷,但不清除狀態標識。這個和interrupt()方法一樣,在後面講中斷機制的文章中專門會講到。

10、join()

講解join()方法之前請確保對於http://www.cnblogs.com/xrq730/p/4853932.html一文,即wait()/notify()/notifyAll()機制已熟練掌握。

join()方法的作用是等待執行緒銷燬。join()方法反應的是一個很現實的問題,比如main執行緒的執行時間是1s,子執行緒的執行時間是10s,但是主執行緒依賴子執行緒執行完的結果,這時怎麼辦?可以像生產者/消費者模型一樣,搞一個緩衝區,子執行緒執行完把資料放在緩衝區中,通知main執行緒,main執行緒去拿,這樣就不會浪費main執行緒的時間了。另外一種方法,就是join()了。先看一下例子:

public class MyThread36 extends Thread{ public void run() { try { int secondValue = (int)(Math.random() * 10000); System.out.println(secondValue); Thread.sleep(secondValue); }  catch (InterruptedException e) { e.printStackTrace(); } }}
public static void main(String[] args) throws Exception{ MyThread36 mt = new MyThread36(); mt.start(); mt.join(); System.out.println("我想當mt物件執行完畢之後我再執行,我做到了");}

看一下執行結果:

3111我想當mt物件執行完畢之後我再執行,我做到了

意思是,join()方法會使呼叫join()方法的執行緒(也就是mt執行緒)所在的執行緒(也就是main執行緒)無限阻塞,直到呼叫join()方法的執行緒銷燬為止,此例中main執行緒就會無限期阻塞直到mt的run()方法執行完畢。

join()方法的一個重點是要區分出和sleep()方法的區別。join(2000)也是可以的,表示呼叫join()方法所在的執行緒最多等待2000ms,兩者的區別在於:

sleep(2000)不釋放鎖,join(2000)釋放鎖,因為join()方法內部使用的是wait(),因此會釋放鎖。看一下join(2000)的原始碼就知道了,join()其實和join(2000)一樣,無非是join(0)而已:

 1 public final synchronized void join(long millis)  2 throws InterruptedException { 3 long base = System.currentTimeMillis(); 4 long now = 0; 5  6 if (millis < 0) { 7 throw new IllegalArgumentException("timeout value is negative"); 8 } 9 10 if (millis == 0) {11 while (isAlive()) {12 wait(0);13 }14 } else {15 while (isAlive()) {16 long delay = millis - now;17 if (delay <= 0) {18 break;19 }20 wait(delay);21 now = System.currentTimeMillis() - base;22 }23 }24 }

第12行、第20行應該已經很清楚了

7367feb60789a85b8368461ff9506557.png