Java基礎-->多線程

一、概述:

1、線程是什麼呢?

我們先來說一說比較熟悉的進程吧,之後就比較容易理解線程了。所謂進程,就是一個正在執行(進行)中的程序。每一個進程的執行都有一個執行順序,或者說是一個控制單元。簡單來說,就是你做一件事所要進行的一套流程。線程,就是進程中的一個獨立的控制單元;也就是說,線程是愛控制着進程的執行。一個進程至少有一個線程,並且線程的出現使得程序要有效率。打個比方說,在倉庫搬運貨物,一個人搬運和五個人搬運效率是不一樣的,搬運貨物的整個程序,就是進程;每一個人搬運貨物的過程,就是線程。

2、java中的線程:

在java中,JVM虛擬機啓動時,會有一個進程爲java.exe,該程序中至少有一個線程負責java程序的執行;而且該程序運行的代碼存在於main方法中,該線程稱之爲主線程。其實,JVM啓動時不止有一個線程(主線程),由於java是具有垃圾回收機制的,所以,在進程中,還有負責垃圾回收機制的線程。

3、多線程的意義:

透過上面的例子,可以看出,多線程有兩方面的意義:

1)提高效率。   2)清除垃圾,解決內存不足的問題。

二、自定義線程:

線程有如此的好處,那要如何才能通過代碼自定義一個線程呢?其實,線程是通過系統創建和分配的,java是不能獨立創建線程的;但是,java是可以通過調用系統,來實現對進程的創建和分配的。java作爲一種面向對象的編程語言,是可以將任何事物描述爲對象,從而進行操作的,進程也不例外。我們通過查閱API文檔,知道java提供了對線程這類事物的描述,即Thread類。創建新執行線程有兩種方法

一)創建線程方式一:繼承Thread類。

1、步驟:

第一、定義類繼承Thread。

第二、複寫Thread類中的run方法。

第三、調用線程的start方法。分配並啓動該子類的實例。

          start方法的作用:啓動線程,並調用run方法。

  1. <span style="font-family: Arial; ">class Demo extends Thread  
  2. {  
  3.     public void run()  
  4.     {  
  5.         for (int i=0;i<60;i++)  
  6.             System.out.println(Thread.currentThread().getName() + "demo run---" + i);  
  7.     }  
  8. }  
  9. class Test2  
  10. {  
  11.     public static void main(String[] args)   
  12.     {  
  13.         Demo d1 = new Demo();//創建一個對象就創建好了一個線程  
  14.         Demo d2 = new Demo();  
  15.         d1.start();//開啓線程並執行run方法  
  16.         d2.start();  
  17.         for (int i=0;i<60;i++)  
  18.             System.out.println("Hello World!---" + i);  
  19.     }  
  20. }</span>  

2、運行特點:

A.併發性:我們看到的程序(或線程)併發執行,其實是一種假象。有一點需要明確:;在某一時刻,只有一個程序在運行(多核除外),此時cpu是在進行快速的切換,以達到看上去是同時運行的效果。由於切換時間是非常短的,所以我們可以認爲是在併發進行。

B.隨機性:在運行時,每次的結果不同。由於多個線程都在獲取cpu的執行權,cpu執行到哪個線程,哪個線程就會執行。可以將多線程運行的行爲形象的稱爲互相搶奪cpu的執行權。這就是多線程的特點,隨機性。執行到哪個程序並不確定。

3、覆蓋run方法的原因:

1)Thread類用於描述線程。該類定義了一個功能:用於存儲線程要運行的代碼,該存儲功能即爲run方法。也就是說,Thread類中的run方法用於存儲線程要運行的代碼,就如同main方法存放的代碼一樣。

2)複寫run的目的:將自定義代碼存儲在run方法中,讓線程運行要執行的代碼。直接調用run,就是對象在調用方法。調用start(),開啓線程並執行該線程的run方法。如果直接調用run方法,只是將線程創建了,但未運行。

二)創建線程方式二:實現Runnable接口

1、步驟:

第一、定義類實現Runnable接口。

第二、覆蓋Runnable接口中的run方法。

第三、通過Thread類建立線程對象。要運行幾個線程,就創建幾個對象。

第四、將Runnable接口的子類對象作爲參數傳遞給Thread類的構造函數。

第五、調用Thread類的start方法開啓線程,並調用Runnable接口子類的run方法。

  1. <span style="font-family: Arial; ">  
  2. //多個窗口同時賣票  
  3. class Ticket implements Runnable  
  4. {  
  5.     private int tic = 20;  
  6.     public void run()  
  7.     {  
  8.         while(true)  
  9.         {  
  10.             if (tic > 0)  
  11.                 System.out.println(Thread.currentThread().getName() + "sale:" + tic--);  
  12.         }  
  13.     }  
  14. }  
  15.   
  16. class  TicketDemo  
  17. {  
  18.     public static void main(String[] args)   
  19.     {  
  20.         Ticket t = new Ticket();  
  21.         Thread t1 = new Thread(t);//創建一個線程  
  22.         Thread t2 = new Thread(t);//創建一個線程  
  23.         Thread t3 = new Thread(t);//創建一個線程  
  24.         Thread t4 = new Thread(t);//創建一個線程  
  25.         t1.start();  
  26.         t2.start();  
  27.         t3.start();  
  28.         t4.start();  
  29.     }  
  30. }  
  31. </span>  

2、說明:

A.步驟2覆蓋run方法:將線程要運行的代碼存放在該run方法中。

B.步驟4:爲何將Runnable接口的子類對象傳給Thread構造函數。因爲自定義的run方法所屬對象爲Runnable接口的子類對象,所以讓線程指定對象的run方法,就必須明確該run方法所屬的對象。

三)實現方式與繼承方式有何區別:

1、實現方式:避免了單繼承的侷限性。

             在定義線程時,建議使用實現方式。

2區別:

繼承Thread:線程代碼存放在Thread子類的run方法中。

實現Runnable:線程代碼存在接口的子類run方法中。

需要注意的是:局部變量在每一個線程中都獨有一份。

四)Thread類中的一些方法簡介:

1、獲取線程名稱:getName()

每個線程都有自己默認的名稱,獲取格式:對象.getName();打印後,顯示爲:Thread-編號(從0開始),也就是說,線程一爲:Thread-0,線程二爲:Thread-1。也可以獲取當前線程對象的名稱,通過currentThread().getName()。如上面方式二的結果爲


2、設置線程名稱:setName()或構造函數

可以通過setName()設置線程名稱,或者通過含有參數的構造函數直接顯式初始化線程的名稱,如Test(String name)。

三、線程的運行狀態

線程運行狀態可用如下圖示說明:


需要說明的是:

A.阻塞狀態:具備運行資格,但是沒有執行權,必須等到cpu的執行權,才轉到運行狀態,。

B.凍結狀態:放棄了cpu的執行資格,cpu不會將執行權分配給這個狀態下的線程,必須被喚醒後,此線程要先轉換到阻塞狀態,等待cpu的執行權後,纔有機會被執行到。

四、多線程的安全問題:

在那個簡單的賣票小程序中,發現打印出了0、-1、-2等錯票,也就是說這樣的多線程在運行的時候是存在一定的安全問題的。

爲什麼會出現這種安全問題呢?

原因是當多條語句在操作同一線程共享數據時,一個線程對多條語句只執行了一部分,還未執行完,另一線程就參與進來執行了,導致共享數據發生錯誤。以也就是說,由於cpu的快速切換,當執行線程一時,tic爲1了,執行到if (tic > 0)的時候,cpu就可能將執行權給了線程二,那麼線程一就停在這條語句了,tic還沒減1,仍爲1;線程二也判斷if (tic > 0)是符合的,也停在這,以此類推。當cpu再次執行線程一的時候,打印的是1號,執行線程二的時候,是2號票,以此類推,就出現了錯票的結果。其實就是多條語句被共享了,如果是一條語句,是不會出現此種情況的。

那麼該如何解決呢?

對於多條操作共享數據的語句,只能讓一個線程都執行完,在執行過程中,其他線程不可參與執行,就不會出現問題了。Java對於多線程的安全問題,提供了專業的解決方式,即同步代碼塊,可操作共享數據。

1、同步代碼塊

格式:

  1. <span style="font-family: Arial; ">  
  2. synchronized(對象)//對象稱爲鎖旗標  
  3. {  
  4.     需要被同步的代碼  
  5. }</span><span style="font-family: SimSun; font-size: 14px; ">  
  6. </span>  
其中的對象如同鎖,持有鎖的線程可在同步中執行,沒有鎖的線程,即使獲得cpu的執行權,也進不去,因爲沒有獲取鎖,是進不去代碼塊中執行共享數據語句的。

1)同步的前提:

A.必須有兩個或兩個以上的線程

B.必須保證同步的線程使用同一個鎖。必須保證同步中只能有一個線程在運行。

好處與弊端:解決了多線程的安全問題。多個線程需要判斷鎖,較爲消耗資源。

示例:

  1. <span style="font-family: Arial; ">class Ticket implements Runnable  
  2. {  
  3.     private int tic = 100;  
  4.     Object obj = new Object();  
  5.     public void run()  
  6.     {  
  7.         while(true)  
  8.         {  
  9.             synchronized(obj)//任意的一個對象  
  10.             {  
  11.                 //此兩句爲共享語句  
  12.                 if (tic > 0)  
  13.                     System.out.println(Thread.currentThread().getName() + "sale:" + tic--);  
  14.             }     
  15.         }  
  16.     }  
  17. }  
  18.   
  19. class  TicketDemo  
  20. {  
  21.     public static void main(String[] args)   
  22.     {  
  23.         Ticket t = new Ticket();  
  24.         Thread t1 = new Thread(t,"1");//創建第一個線程  
  25.         Thread t2 = new Thread(t,"2");//創建第二個線程  
  26.         //開啓線程  
  27.         t1.start();  
  28.         t2.start();  
  29.     }  
  30. }  
  31.   
  32. </span>  

2、同步函數

同步函數就是將修飾符synchronized放在返回類型的前面,下面通過同步函數給出多線程安全問題的具體解決方案:

1)目的:判斷程序中是否有安全問題,若有,該如何解決。

2)解決:第一、明確哪些代碼是多線程的運行代碼

                  第二、明確共享數據

                  第三、明確多線程運行代碼中,哪些語句是操作共享數據的。

示例:

  1. <span style="font-family: Arial; ">class Bank  
  2. {  
  3.     private int sum;//共享數據  
  4.     //run中調用了add,所以其也爲多線程運行代碼  
  5.     public synchronized void add(int n)//同步函數,用synchronized修飾  
  6.     {  
  7.         //這有兩句操作,是操作共享數據的  
  8.         sum += n;  
  9.             System.out.println("sum" + sum);  
  10.     }  
  11. }  
  12.   
  13. class Cus implements Runnable  
  14. {  
  15.     private Bank b = new Bank();//共享數據  
  16.     //多線程運行代碼run  
  17.     public void run()  
  18.     {  
  19.         for (int i=0;i<3;i++)  
  20.         {  
  21.             b.add(100);//一句,不會分開執行,所以沒問題  
  22.         }  
  23.     }  
  24. }  
  25.   
  26. class BankDemo   
  27. {  
  28.     public static void main(String[] args)   
  29.     {  
  30.         Cus c = new Cus();  
  31.         Thread t1 = new Thread(c);  
  32.         Thread t2 = new Thread(c);  
  33.         t1.start();  
  34.         t2.start();  
  35.     }  
  36. }  
  37. </span>  

五、同步函數中的鎖:

1、非靜態同步函數中的鎖---> this

函數需被對象調用,那麼函數都有一個所屬的對象引用,就是this,因此同步函數使用的鎖爲this。測驗如下:

  1. <span style="font-family: Arial; ">  
  2. class Ticket implements Runnable  
  3. {  
  4.     private int tic = 100;  
  5.     boolean flog = true;  
  6.     public void run()  
  7.     {  
  8.         if (flog)  
  9.         {  
  10.             //線程一執行  
  11.             while(true)  
  12.             {  
  13.                 //如果對象爲obj,則是兩個鎖,是不安全的;換成this,爲一個鎖,會安全很多  
  14.                 synchronized(this)  
  15.                 {  
  16.                     if (tic > 0)  
  17.                         System.out.println(Thread.currentThread().getName() + "--cobe--:" + tic--);  
  18.                 }  
  19.             }  
  20.         }  
  21.         //線程二執行  
  22.         else  
  23.             while(true)  
  24.                 show();  
  25.     }  
  26.     public synchronized void show()  
  27.     {  
  28.         if (tic > 0)  
  29.             System.out.println(Thread.currentThread().getName() + "----show-----:" + tic--);  
  30.     }  
  31. }  
  32.   
  33. class ThisLockDemo  
  34. {  
  35.     public static void main(String[] args)   
  36.     {  
  37.         Ticket t = new Ticket();  
  38.         Thread t1 = new Thread(t);//創建一個線程  
  39.         Thread t2 = new Thread(t);//創建一個線程  
  40.         t1.start();  
  41.         t.flog = false;//開啓線程一,即關閉if,讓線程二執行else中語句  
  42.         t2.start();  
  43.     }  
  44. }</span>  

讓線程一執行打印cobe的語句,讓線程二執行打印show的語句。如果對象換位另一個對象obj,那將是兩個鎖,因爲在主函數中創建了一個對象即Ticket t = new Ticket();,線程會共享這個對象調用的run方法中的數據,所以都是這個t對象在調用,那麼,其中的對象應爲this;否則就破壞了同步的前提,就會出現安全問題。

2、靜態同步函數中的鎖:

如果同步函數被靜態修飾後,經驗證,使用的鎖不是this了,因爲靜態方法中不可定義this,所以,這個鎖不再是this了。靜態進內存時,內存中沒有本類對象,但是一定有該類對應的字節碼文件對象:類名.class;該對象的類型是Class。

所以靜態的同步方法使用的鎖是該方法所在類的字節碼文件對象,即類名.class。

示例:

  1. <span style="font-family: Arial; ">class Ticket implements Runnable  
  2. {  
  3.     //私有變量,共享數據  
  4.     private static int tic = 100;  
  5.     boolean flog = true;  
  6.     public void run()  
  7.     {  
  8.         //線程一執行  
  9.         if (flog)  
  10.         {  
  11.             while(true)  
  12.             {  
  13.                 synchronized(Ticket.class)//不再是this了,是Ticket.class  
  14.                 {  
  15.                     if (tic > 0)  
  16.                         System.out.println(Thread.currentThread().getName() + "--obj--:" + tic--);  
  17.                 }  
  18.             }  
  19.         }  
  20.         //線程二執行  
  21.         else  
  22.             while(true)  
  23.                 show();  
  24.     }  
  25.     public static synchronized void show()  
  26.     {  
  27.         if (tic > 0)  
  28.             System.out.println(Thread.currentThread().getName() + "----show-----:" + tic--);  
  29.     }  
  30. }  
  31.   
  32. class StaticLockDemo  
  33. {  
  34.     public static void main(String[] args)   
  35.     {  
  36.         Ticket t = new Ticket();  
  37.         Thread t1 = new Thread(t);//創建第一個線程  
  38.         Thread t2 = new Thread(t);//創建第二個線程  
  39.         t1.start();  
  40.         t.flog = false;  
  41.         t2.start();  
  42.     }  
  43. }  
  44. </span>  

在之前,也提到過關於多線程的安全問題的相關知識,就是在單例設計模式中的懶漢式中,用到了鎖的機制。

具體請看單例設計模式中的內容:http://blog.csdn.net/shengfeixiang/article/details/8592722


六、多線程間的通信:

多線程間通信是線程之間進行交互的方式,簡單說就是存儲資源和獲取資源。比如說倉庫中的貨物,有進貨的,有出貨的。還比如生產者和消費者的例子。這些都可以作爲線程通信的實例。那麼如何更好地實現通信呢?先看下面的代碼:

  1. /* 
  2. 線程間通信: 
  3.  
  4. 等待喚醒機制:升級版 
  5.  
  6. 生產者消費者  多個 
  7.  
  8. */  
  9. import java.util.concurrent.locks.*;  
  10.   
  11. class ProducerConsumerDemo{  
  12.     public static void main(String[] args){  
  13.         Resouse r = new Resouse();  
  14.         Producer p = new Producer(r);  
  15.         Consumer c = new Consumer(r);  
  16.         Thread t1 = new Thread(p);  
  17.         Thread t2 = new Thread(c);  
  18.         Thread t3 = new Thread(p);  
  19.         Thread t4 = new Thread(c);  
  20.         t1.start();  
  21.         t2.start();  
  22.         t3.start();  
  23.         t4.start();  
  24.     }  
  25. }  
  26.   
  27. class Resouse{  
  28.     private String name;  
  29.     private int count = 1;  
  30.     private boolean flag =  false;   
  31.     private Lock lock = new ReentrantLock();  
  32.     private Condition condition_P = lock.newCondition();  
  33.     private Condition condition_C = lock.newCondition();  
  34. //要喚醒全部,否則都可能處於凍結狀態,那麼程序就會停止。這和死鎖有區別的。  
  35.     public void set(String name)throws InterruptedException{  
  36.         lock.lock();  
  37.         try{  
  38.             while(flag)//循環判斷,防止都凍結狀態  
  39.                 condition_P.await();  
  40.             this.name = name + "--" + count++;  
  41.             System.out.println(Thread.currentThread().getName() + "..生成者--" + this.name);  
  42.             flag = true;  
  43.             condition_C.signal();  
  44.         }finally{  
  45.             lock.unlock();//釋放鎖的機制一定要執行  
  46.         }         
  47.     }  
  48.     public void out()throws InterruptedException{  
  49.         lock.lock();  
  50.         try{  
  51.             while(!flag)//循環判斷,防止都凍結狀態  
  52.                 condition_C.await();  
  53.             System.out.println(Thread.currentThread().getName() + "..消費者." + this.name);  
  54.             flag = false;  
  55.             condition_P.signal();//喚醒全部  
  56.         }finally{  
  57.             lock.unlock();  
  58.         }  
  59.     }  
  60. }  
  61.   
  62. class Producer implements Runnable{  
  63.     private Resouse r;  
  64.     Producer(Resouse r){  
  65.         this.r = r;  
  66.     }  
  67.     public void run(){  
  68.         while(true){  
  69.             try{  
  70.                 r.set("--商品--");  
  71.             }catch (InterruptedException e){}  
  72.         }  
  73.     }  
  74. }  
  75.   
  76. class Consumer implements Runnable{  
  77.     private Resouse r;  
  78.     Consumer(Resouse r){  
  79.         this.r = r;  
  80.     }  
  81.     public void run(){  
  82.         while(true){  
  83.             try{  
  84.                 r.out();  
  85.             }catch (InterruptedException e){}  
  86.         }  
  87.     }  
  88. }  


一)等待喚醒機制:

1、顯式鎖機制和等待喚醒機制:

在JDK 1.5中,提供了改進synchronized的升級解決方案。將同步synchronized替換爲顯式的Lock操作,將Object中的wait,notify,notifyAll替換成Condition對象,該對象可對Lock鎖進行獲取。這就實現了本方喚醒對方的操作。在這裏說明幾點:

1)、對於wait,notify和notifyAll這些方法都是用在同步中,也就是等待喚醒機制,這是因爲要對持有監視器(鎖)的線程操作。所以要使用在同步中,因爲只有同步才具有鎖。

2)、而這些方法都定義在Object中,是因爲這些方法操作同步中的線程時,都必須表示自己所操作的線程的鎖,就是說,等待和喚醒的必須是同一把鎖。不可對不同鎖中的線程進行喚醒。所以這就使得程序是不良的,因此,通過對鎖機制的改良,使得程序得到優化。

3)、等待喚醒機制中,等待的線程處於凍結狀態,是被放在線程池中,線程池中的線程已經放棄了執行資格,需要被喚醒後,纔有被執行的資格。

2、對於上面的程序,有兩點要說明:

1)、爲何定義while判斷標記:

原因是讓被喚醒的線程再判斷一次。

避免未經判斷,線程不知是否應該執行,就執行本方的上一個已經執行的語句。如果用if,消費者在等着,兩個生成着一起判斷完flag後,cpu切換到其中一個如t1,另一個t3在wait,當t1喚醒凍結中的一個,是t3(因爲它先被凍結的,就會先被喚醒),所以t3未經判斷,又生產了一個。而沒消費。

2)這裏使用的是signal方法,而不是signalAll方法。是因爲通過Condition的兩個對象,分別喚醒對方,這就體現了Lock鎖機制的靈活性。可以通過Contidition對象調用Lock接口中的方法,就可以保證多線程間通信的流暢性了。

二)Thread類中的方法簡介:

在這簡單介紹幾個Thread中的方法:

1、停止線程:

在java 1.5之後,就不再使用stop方法停止線程了。那麼該如何停止線程呢?只有一種方法,就是讓run方法結束。

開啓多線程運行,運行代碼通常爲循環結構,只要控制住循環,就可以讓run方法結束,也就可以使線程結束。

注:

特殊情況:當線程處於凍結狀態,就不會讀取標記,那麼線程就不會結束。如下:

  1. <span style="font-family: Arial; ">class StopThread implements Runnable{  
  2.     private boolean flag = true;  
  3.     public synchronized void run(){  
  4.         while (flag){  
  5.             try{  
  6.                 wait();  
  7.             }catch (InterruptedException e) {  
  8.                 System.out.println(Thread.currentThread().getName() + "----Exception");  
  9.                 flag = false;  
  10.             }  
  11.             System.out.println(Thread.currentThread().getName() + "----run");  
  12.         }  
  13.     }  
  14.     public void changeFlag(){  
  15.         flag = false;  
  16.     }  
  17. }  
  18.   
  19. class  StopThreadDemo{  
  20.     public static void main(String[] args) {  
  21.         StopThread st = new StopThread();  
  22.   
  23.         Thread t1 = new Thread(st);  
  24.         Thread t2 = new Thread(st);  
  25.           
  26.         t1.start();  
  27.         t2.start();  
  28.   
  29.         int n = 0;  
  30.         while (true){  
  31.             if (n++ == 60){  
  32.                 st.changeFlag();  
  33.                 break;  
  34.             }  
  35.             System.out.println("Hello World!");  
  36.         }  
  37.     }  
  38. }</span>  

這時,當沒有指定的方式讓凍結的線程回覆打破運行狀態時,就需要對凍結進行清除。強制讓線程回覆到運行狀態來,這樣就可以操作標記讓線程結束。

在Thread類中提供了此種方法:interrupt()。此方法是爲了讓線程中斷,但是並沒有結束運行,讓線程恢復到運行狀態,再判斷標記從而停止循環,run方法結束,線程結束。

  1. <span style="font-family: Arial; ">class StopThread implements Runnable{  
  2.     private boolean flag = true;  
  3.     public synchronized void run(){  
  4.         while (flag){  
  5.             try{  
  6.                 wait();  
  7.             }catch (InterruptedException e){  
  8.                 System.out.println(Thread.currentThread().getName() + "----Exception");  
  9.                 flag = false;  
  10.             }  
  11.             System.out.println(Thread.currentThread().getName() + "----run");  
  12.         }  
  13.     }  
  14. }  
  15.   
  16. class  StopThreadDemo{  
  17.     public static void main(String[] args){  
  18.         StopThread st = new StopThread();  
  19.         Thread t1 = new Thread(st);  
  20.         Thread t2 = new Thread(st);       
  21.         t1.start();  
  22.         t2.start();  
  23.         int n = 0;  
  24.         while (true){  
  25.             if (n++ == 60){  
  26.                 t1.interrupt();  
  27.                 t2.interrupt();  
  28.                 break;  
  29.             }  
  30.             System.out.println("Hello World!");  
  31.         }  
  32.     }  
  33. }  
  34. </span>  

2、守護線程:---setDaemon()

可將一個線程標記爲守護線程,直接調用這個方法。此方法需要在啓動前調用守護線程在這個線程結束後,會自動結束,則Jvm虛擬機也結束運行。

  1. <span style="font-family: Arial; ">       ........  
  2.       
  3.     //守護線程(後臺線程),在啓動前調用。後臺線程自動結束  
  4.     t1.setDaemon(true);  
  5.     t2.setDaemon(true);  
  6.     t1.start();  
  7.     t2.start();  
  8.   
  9.       .........</span>  

3、臨時加入線程:--join()

特點:當A線程執行到B線程方法時,A線程就會等待,B線程都執行完,A纔會執行。join可用來臨時加入線程執行。

  1. <span style="font-family: Arial; ">class Demo implements Runnable{  
  2.     public void run(){  
  3.         for(int x=0;x<90;x++){  
  4.             System.out.println(Thread.currentThread().getName() + "----run" + x);  
  5.         }  
  6.     }  
  7. }  
  8.   
  9. class  JoinDemo{  
  10.     public static void main(String[] args)throws Exception{  
  11.         Demo d = new Demo();  
  12.         Thread t1 = new Thread(d);  
  13.         Thread t2 = new Thread(d);  
  14.         t1.start();       
  15.         t2.start();  
  16.         t1.join();//等t1執行完了,主線程才從凍結狀態恢復,和t2搶執行權。t2執不執行完都無所謂。  
  17.         int n = 0;  
  18.         for(int x=0;x<80;x++){  
  19.             System.out.println(Thread.currentThread().getName() + "----main" + x);  
  20.         }  
  21.         System.out.println("Over");  
  22.     }  
  23. }  
  24. </span>  

4、優先級:

setPriority():

在Thread中,存在着1~10這十個執行級別,最高的是 MAX_PRIORITY 爲10,最低是 MIN_PRIORITY 爲1,默認優先級是 NORM_PRIORITY 爲5;但是並不是優先級越高,就會一直執行這個線程,只是說會優先執行到這個線程,此後還是有其他線程會和此線程搶奪cpu執行權的。

優先級是可以設定的,可通過setPriority()設定,如:setPriority(Thread.MAX_PRIORITY)設優先級爲最大。

yield():

此方法可暫停當前線程,而執行其他線程。通過這個方法,可稍微減少線程執行頻率,達到線程都有機會平均被執行的效果。如下:

  1. <span style="font-family: Arial; ">class Demo implements Runnable{  
  2.     public void run(){  
  3.         for(int x=0;x<90;x++){  
  4.             System.out.println(Thread.currentThread().toString() + "----run" + x);  
  5.             Thread.yield();//稍微減少線程執行頻率。可達到線程都有機會達到平均運行的效果  
  6.         }  
  7.     }  
  8. }  
  9.   
  10. class  YieldDemo{  
  11.     public static void main(String[] args)throws Exception{  
  12.         Demo d = new Demo();  
  13.         Thread t1 = new Thread(d);  
  14.         Thread t2 = new Thread(d);  
  15.         t1.start();  
  16.         t1.setPriority(Thread.MAX_PRIORITY);//設置線程優先級最大       
  17.         t2.start();  
  18.         System.out.println("Over");  
  19.     }  
  20. }</span>  


對於多線程的知識,還需要慢慢積累,畢竟線程通信可以提高程序運行的效率,這樣就可以讓程序得到很大的優化。期待新知識······

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章