黑馬程序員——多線程

------- android培訓java培訓、期待與您交流! ----------

一、多線程概述

        要理解多線程,就必須理解線程。而要理解線程,就必須知道進程。

1、 進程

        是一個正在執行的程序。

        每一個進程執行都有一個執行順序。該順序是一個執行路徑,或者叫一個控制單元。

2、線程

         就是進程中的一個獨立的控制單元。線程在控制着進程的執行。只要進程中有一個線程在執行,進程就不會結束。

        一個進程中至少有一個線程。

3、多線程

        java虛擬機啓動的時候會有一個java.exe的執行程序,也就是一個進程。該進程中至少有一個線程負責java程序的執行。而且這個線程運行的代碼存在於main方法中。該線程稱之爲主線程。JVM啓動除了執行一個主線程,還有負責垃圾回收機制的線程。像種在一個進程中有多個線程執行的方式,就叫做多線程。

4、多線程存在的意義

        多線程的出現能讓程序產生同時運行效果。可以提高程序執行效率。

         例如:在java.exe進程執行主線程時,如果程序代碼特別多,在堆內存中產生了很多對象,而同時對象調用完後,就成了垃圾。如果垃圾過多就有可能是堆內存出現內存不足的現象,只是如果只有一個線程工作的話,程序的執行將會很低效。而如果有另一個線程幫助處理的話,如垃圾回收機制線程來幫助回收垃圾的話,程序的運行將變得更有效率。

5、計算機CPU的運行原理

         我們電腦上有很多的程序在同時進行,就好像cpu在同時處理這所以程序一樣。但是,在一個時刻,單核的cpu只能運行一個程序。而我們看到的同時運行效果,只是cpu在多個進程間做着快速切換動作。

         cpu執行哪個程序,是毫無規律性的。這也是多線程的一個特性:隨機性。哪個線程被cpu執行,或者說搶到了cpu的執行權,哪個線程就執行。而cpu不會只執行一個,當執行一個一會後,又會去執行另一個,或者說另一個搶走了cpu的執行權。至於究竟是怎麼樣執行的,只能由cpu決定。

 

二、創建線程的方式

        創建線程共有兩種方式:繼承方式和實現方式(簡單的說)。

1、 繼承方式

        通過查找java的幫助文檔API,我們發現java中已經提供了對線程這類事物的描述的類——Thread類。這第一種方式就是通過繼承Thread類,然後複寫其run方法的方式來創建線程。

創建步驟:

        a,定義類繼承Thread

        b,複寫Thread中的run方法。

             目的:將自定義代碼存儲在run方法中,讓線程運行。

        c,創建定義類的實例對象。相當於創建一個線程。

        d,用該對象調用線程的start方法。該方法的作用是:啓動線程,調用run方法。

注:如果對象直接調用run方法,等同於只有一個線程在執行,自定義的線程並沒有啓動。

覆蓋run方法的原因:

        Thread類用於描述線程。該類就定義了一個功能,用於存儲線程要執行的代碼。該存儲功能就run方法。也就是說,Thread類中的run方法,用於存儲線程要運行的代碼。

程序示例:

[java] view plaincopy
  1. /* 
  2. 小練習 
  3. 創建兩線程,和主線程交替運行。 
  4. */  
  5.   
  6. //創建線程Test  
  7. class Test extends Thread  
  8. {  
  9. //  private String name;  
  10.     Test(String name)  
  11.     {  
  12.         super(name);  
  13. //      this.name=name;  
  14.     }  
  15.     //複寫run方法  
  16.     public void run()  
  17.     {  
  18.         for(int x=0;x<60;x++)  
  19.         System.out.println(Thread.currentThread().getName()+"..run..."+x);  
  20. //      System.out.println(this.getName()+"..run..."+x);  
  21.     }  
  22. }  
  23.   
  24. class  ThreadTest  
  25. {  
  26.     public static void main(String[] args)   
  27.     {  
  28.         new Test("one+++").start();//開啓一個線程  
  29.   
  30.         new Test("tow———").start();//開啓第二線程  
  31.   
  32.         //主線程執行的代碼  
  33.         for(int x=0;x<170;x++)  
  34.         System.out.println("Hello World!");  
  35.     }  
  36. }  

結果:

      如圖,執行是隨機、交替執行的,每一次運行的結果都會不同。

       

2、 實現方式

        使用繼承方式有一個弊端,那就是如果該類本來就繼承了其他父類,那麼就無法通過Thread類來創建線程了。這樣就有了第二種創建線程的方式:實現Runnable接口,並複習其中run方法的方式。

創建步驟:

        a,定義類實現Runnable的接口。

        b,覆蓋Runnable接口中的run方法。目的也是爲了將線程要運行的代碼存放在該run方法中。

        c,通過Thread類創建線程對象。

        d,將Runnable接口的子類對象作爲實參傳遞給Thread類的構造方法。

       爲什麼要將Runnable接口的子類對象傳遞給Thread的構造函數?

        因爲,自定義的run方法所屬的對象是Runnable接口的子類對象。所以要讓線程去指定對象的run方法,就必須明確該run方法所屬對象。

        e,調用Thread類中start方法啓動線程。start方法會自動調用Runnable接口子類的run方法。

實現方式好處:避免了單繼承的侷限性。在定義線程時,建議使用實現方式。 

程序示例:

[java] view plaincopy
  1. /* 
  2. 需求:簡單的賣票程序。 
  3. 多個窗口賣票。 
  4. */  
  5. class Ticket implements Runnable//extends Thread  
  6. {  
  7.     private  int tick = 100;  
  8.     public void run()  
  9.     {  
  10.         while(true)  
  11.         {  
  12.             if(tick>0)  
  13.             {  
  14.                 //顯示線程名及餘票數  
  15.                 System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);  
  16.             }  
  17.         }  
  18.     }  
  19. }  
  20.   
  21.   
  22. class  TicketDemo  
  23. {  
  24.     public static void main(String[] args)   
  25.     {  
  26.         //創建Runnable接口子類的實例對象  
  27.         Ticket t = new Ticket();  
  28.   
  29.         //有多個窗口在同時賣票,這裏用四個線程表示  
  30.         Thread t1 = new Thread(t);//創建了一個線程  
  31.         Thread t2 = new Thread(t);  
  32.         Thread t3 = new Thread(t);  
  33.         Thread t4 = new Thread(t);  
  34.   
  35.         t1.start();//啓動線程  
  36.         t2.start();  
  37.         t3.start();  
  38.         t4.start();  
  39.     }  
  40. }  

 

三、兩種方式的區別和線程的幾種狀態

1、兩種創建方式的區別

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

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

2、幾種狀態

        被創建:等待啓動,調用start啓動。

         運行狀態:具有執行資格和執行權。

         臨時狀態(阻塞):有執行資格,但是沒有執行權。

         凍結狀態:遇到sleeptime)方法和wait()方法時,失去執行資格和執行權,sleep方法時間到或者調用notify()方法時,獲得執行資格,變爲臨時狀態。

         消忙狀態:stop()方法,或者run方法結束。

注:當已經從創建狀態到了運行狀態,再次調用start()方法時,就失去意義了,java運行時會提示線程狀態異常。

圖解:

   

四、線程安全問題

1、導致安全問題的出現的原因:

        當多條語句在操作同一線程共享數據時,一個線程對多條語句只執行了一部分,還沒用執行完,另一個線程參與進來執行。導致共享數據的錯誤。

簡單的說就兩點:

        a、多個線程訪問出現延遲。

        b、線程隨機性    

注:線程安全問題在理想狀態下,不容易出現,但一旦出現對軟件的影響是非常大的。

2、解決辦法——同步

        對多條操作共享數據的語句,只能讓一個線程都執行完。在執行過程中,其他線程不可以參與執行。

        在java中對於多線程的安全問題提供了專業的解決方式——synchronized(同步)

        這裏也有兩種解決方式,一種是同步代碼塊,還有就是同步函數。都是利用關鍵字synchronized來實現。

         a、同步代碼塊

        用法:

                  synchronized(對象)

                  {需要被同步的代碼}

        同步可以解決安全問題的根本原因就在那個對象上。其中對象如同鎖。持有鎖的線程可以在同步中執行。沒有持有鎖的線程即使獲取cpu的執行權,也進不去,因爲沒有獲取鎖。

示例:

[java] view plaincopy
  1. /*   
  2. 給賣票程序示例加上同步代碼塊。 
  3. */  
  4. class Ticket implements Runnable  
  5. {  
  6.     private int tick=100;  
  7.     Object obj = new Object();  
  8.     public void run()  
  9.     {  
  10.         while(true)  
  11.         {  
  12.             //給程序加同步,即鎖  
  13.             synchronized(obj)  
  14.             {  
  15.                 if(tick>0)  
  16.                 {  
  17.                     try  
  18.                     {     
  19.                         //使用線程中的sleep方法,模擬線程出現的安全問題  
  20.                         //因爲sleep方法有異常聲明,所以這裏要對其進行處理  
  21.                         Thread.sleep(10);  
  22.                     }  
  23.                     catch (Exception e)  
  24.                     {  
  25.                     }  
  26.                     //顯示線程名及餘票數  
  27.                     System.out.println(Thread.currentThread().getName()+"..tick="+tick--);  
  28.                 }  
  29.             }     
  30.         }  
  31.     }  
  32. }  

        b,同步函數

        格式:

                在函數上加上synchronized修飾符即可。

        那麼同步函數用的是哪一個鎖呢?

        函數需要被對象調用。那麼函數都有一個所屬對象引用。就是this。所以同步函數使用的鎖是this

拿同步代碼塊的示例:

[java] view plaincopy
  1. class Ticket implements Runnable  
  2. {  
  3.     private int tick=100;  
  4.     Object obj = new Object();  
  5.     public void run()  
  6.     {  
  7.         while(true)  
  8.         {  
  9.             show();  
  10.         }  
  11.     }  
  12.   //直接在函數上用synchronized修飾即可實現同步  
  13. public synchronized void show()  
  14. {  
  15.         if(tick>0)  
  16.         {  
  17.         try  
  18.         {     
  19.             //使用線程中的sleep方法,模擬線程出現的安全問題  
  20.             //因爲sleep方法有異常聲明,所以這裏要對其進行處理  
  21.             Thread.sleep(10);  
  22.         }  
  23.         catch (Exception e)  
  24.         {  
  25.         }  
  26.         //顯示線程名及餘票數  
  27.         System.out.println(Thread.currentThread().getName()+"..tick="+tick--);  
  28.     }  
  29. }     
  30. }  

3、同步的前提

        a,必須要有兩個或者兩個以上的線程。

        b,必須是多個線程使用同一個鎖。

4、同步的利弊

        好處:解決了多線程的安全問題。

        弊端:多個線程需要判斷鎖,較爲消耗資源。

5、如何尋找多線程中的安全問題

        a,明確哪些代碼是多線程運行代碼。

        b,明確共享數據。

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

 

五、靜態函數的同步方式

        如果同步函數被靜態修飾後,使用的鎖是什麼呢?

        通過驗證,發現不在是this。因爲靜態方法中也不可以定義this。靜態進內存時,內存中沒有本類對象,但是一定有該類對應的字節碼文件對象。如:

        類名.class 該對象的類型是Class

這就是靜態函數所使用的鎖。而靜態的同步方法,使用的鎖是該方法所在類的字節碼文件對象。類名.class

經典示例:

[java] view plaincopy
  1. /* 
  2. 加同步的單例設計模式————懶漢式 
  3. */  
  4. class Single  
  5. {  
  6.     private static Single s = null;  
  7.     private Single(){}  
  8.     public static void getInstance()  
  9.     {  
  10.         if(s==null)  
  11.         {  
  12.             synchronized(Single.class)  
  13.             {  
  14.                 if(s==null)  
  15.                     s = new Single();  
  16.             }  
  17.         }  
  18.         return s;  
  19.     }  
  20. }  

 

六、死鎖

        當同步中嵌套同步時,就有可能出現死鎖現象。

示例:

[java] view plaincopy
  1. /* 
  2. 寫一個死鎖程序 
  3. */  
  4.   
  5. //定義一個類來實現Runnable,並複寫run方法  
  6. class LockTest implements Runnable  
  7. {  
  8.     private boolean flag;  
  9.     LockTest(boolean flag)  
  10.     {  
  11.         this.flag=flag;  
  12.     }  
  13.     public void run()  
  14.     {  
  15.         if(flag)  
  16.         {  
  17.             while(true)  
  18.             {  
  19.                 synchronized(LockClass.locka)//a鎖  
  20.                 {  
  21.                     System.out.println(Thread.currentThread().getName()+"------if_locka");  
  22.   
  23.                     synchronized(LockClass.lockb)//b鎖  
  24.                     {  
  25.                     System.out.println(Thread.currentThread().getName()+"------if_lockb");  
  26.                     }  
  27.                 }  
  28.             }  
  29.         }  
  30.         else  
  31.         {  
  32.             while(true)  
  33.             {  
  34.                 synchronized(LockClass.lockb)//b鎖  
  35.                 {  
  36.                   System.out.println(Thread.currentThread().getName()+"------else_lockb");  
  37.   
  38.                     synchronized(LockClass.locka)//a鎖  
  39.                     {  
  40.                    System.out.println(Thread.currentThread().getName()+"------else_locka");  
  41.                     }  
  42.                 }  
  43.             }  
  44.         }  
  45.     }  
  46. }  
  47.   
  48. //定義兩個鎖  
  49. class LockClass  
  50. {  
  51.     static Object locka = new Object();  
  52.     static Object lockb = new Object();  
  53. }  
  54.   
  55. class DeadLock  
  56. {  
  57.     public static void main(String[] args)  
  58.     {  
  59.         //創建2個進程,並啓動  
  60.         new Thread(new LockTest(true)).start();  
  61.         new Thread(new LockTest(false)).start();  
  62.     }  
  63. }  

結果:程序卡住,不能繼續執行

       

 

七、線程間通信

        其實就是多個線程在操作同一個資源,但是操作的動作不同。

1、使用同步操作同一資源的示例:

[java] view plaincopy
  1. /* 
  2.     有一個資源 
  3. 一個線程往裏存東西,如果裏邊沒有的話 
  4. 一個線程往裏取東西,如果裏面有得話 
  5. */  
  6.   
  7. //資源  
  8. class Resource  
  9. {  
  10.     private String name;  
  11.     private String sex;  
  12.     private boolean flag=false;  
  13.       
  14.     public synchronized void setInput(String name,String sex)  
  15.     {  
  16.         if(flag)  
  17.         {  
  18.             try{wait();}catch(Exception e){}//如果有資源時,等待資源取出  
  19.         }  
  20.         this.name=name;  
  21.         this.sex=sex;  
  22.   
  23.         flag=true;//表示有資源  
  24.         notify();//喚醒等待  
  25.     }  
  26.   
  27.     public synchronized void getOutput()  
  28.     {         
  29.         if(!flag)  
  30.         {  
  31.             try{wait();}catch(Exception e){}//如果木有資源,等待存入資源  
  32.         }  
  33.         System.out.println("name="+name+"---sex="+sex);//這裏用打印表示取出  
  34.                   
  35.         flag=false;//資源已取出  
  36.         notify();//喚醒等待  
  37.     }  
  38. }  
  39.   
  40.   
  41. //存線程  
  42. class Input implements Runnable  
  43. {  
  44.     private Resource r;  
  45.     Input(Resource r)  
  46.     {  
  47.         this.r=r;  
  48.     }  
  49.     public void run()//複寫run方法  
  50.     {  
  51.         int x=0;  
  52.         while(true)  
  53.         {  
  54.             if(x==0)//交替打印張三和王羲之  
  55.             {  
  56.                 r.setInput("張三",".....man");  
  57.             }  
  58.             else  
  59.             {  
  60.                 r.setInput("王羲之","..woman");  
  61.             }  
  62.             x=(x+1)%2;//控制交替打印  
  63.         }  
  64.     }  
  65. }  
  66.   
  67. //取線程  
  68. class Output implements Runnable  
  69. {  
  70.     private Resource r;  
  71.     Output(Resource r)  
  72.     {  
  73.         this.r=r;  
  74.     }  
  75.     public void run()//複寫run方法  
  76.     {  
  77.         while(true)  
  78.         {  
  79.             r.getOutput();  
  80.         }  
  81.     }  
  82. }  
  83.   
  84.   
  85.   
  86. class ResourceDemo2   
  87. {  
  88.     public static void main(String[] args)   
  89.     {  
  90.         Resource r = new Resource();//表示操作的是同一個資源  
  91.   
  92.         new Thread(new Input(r)).start();//開啓存線程  
  93.   
  94.         new Thread(new Output(r)).start();//開啓取線程  
  95.     }  
  96. }  

結果:部分截圖

 幾個小問題:

        1)wait(),notify(),notifyAll(),用來操作線程爲什麼定義在了Object類中?

                a,這些方法存在與同步中。

                b,使用這些方法時必須要標識所屬的同步的鎖。同一個鎖上wait的線程,只可以被同一個鎖上的notify喚醒。

                c,鎖可以是任意對象,所以任意對象調用的方法一定定義Object類中。

        2)wait(),sleep()有什麼區別?

              wait():釋放cpu執行權,釋放鎖。

              sleep():釋放cpu執行權,不釋放鎖。

        3)爲甚麼要定義notifyAll

        因爲在需要喚醒對方線程時。如果只用notify,容易出現只喚醒本方線程的情況。導致程序中的所以線程都等待。

2JDK1.5中提供了多線程升級解決方案。

        將同步synchronized替換成顯示的Lock操作。將ObjectwaitnotifynotifyAll,替換成了Condition對象。該Condition對象可以通過Lock鎖進行獲取,並支持多個相關的Condition對象。

升級解決方案的示例:

[java] view plaincopy
  1. /* 
  2. 生產者生產商品,供消費者使用 
  3. 有兩個或者多個生產者,生產一次就等待消費一次 
  4. 有兩個或者多個消費者,等待生產者生產一次就消費掉 
  5.  
  6. */  
  7.   
  8. import java.util.concurrent.locks.*;  
  9.   
  10. class Resource   
  11. {     
  12.     private String name;  
  13.     private int count=1;  
  14.     private boolean flag = false;  
  15.       
  16.     //多態  
  17.     private Lock lock=new ReentrantLock();  
  18.   
  19.     //創建兩Condition對象,分別來控制等待或喚醒本方和對方線程  
  20.     Condition condition_pro=lock.newCondition();  
  21.     Condition condition_con=lock.newCondition();  
  22.   
  23.     //p1、p2共享此方法  
  24.     public void setProducer(String name)throws InterruptedException  
  25.     {  
  26.         lock.lock();//鎖  
  27.         try  
  28.         {  
  29.             while(flag)//重複判斷標識,確認是否生產  
  30.                 condition_pro.await();//本方等待  
  31.   
  32.             this.name=name+"......"+count++;//生產  
  33.             System.out.println(Thread.currentThread().getName()+"...生產..."+this.name);//打印生產  
  34.             flag=true;//控制生產\消費標識  
  35.             condition_con.signal();//喚醒對方  
  36.         }  
  37.         finally  
  38.         {  
  39.             lock.unlock();//解鎖,這個動作一定執行  
  40.         }  
  41.           
  42.     }  
  43.   
  44.     //c1、c2共享此方法  
  45.     public void getConsumer()throws InterruptedException  
  46.     {  
  47.         lock.lock();  
  48.         try  
  49.         {  
  50.             while(!flag)//重複判斷標識,確認是否可以消費  
  51.                 condition_con.await();  
  52.   
  53.             System.out.println(Thread.currentThread().getName()+".消費."+this.name);//打印消費  
  54.             flag=false;//控制生產\消費標識  
  55.             condition_pro.signal();  
  56.         }  
  57.         finally  
  58.         {  
  59.             lock.unlock();  
  60.         }  
  61.   
  62.     }  
  63. }  
  64.   
  65. //生產者線程  
  66. class Producer implements Runnable   
  67. {  
  68.     private Resource res;  
  69.     Producer(Resource res)  
  70.     {  
  71.         this.res=res;  
  72.     }  
  73.     //複寫run方法  
  74.     public void run()  
  75.     {  
  76.         while(true)  
  77.         {  
  78.             try  
  79.             {  
  80.                 res.setProducer("商品");  
  81.             }  
  82.             catch (InterruptedException e)  
  83.             {  
  84.             }  
  85.         }  
  86.     }  
  87. }  
  88.   
  89. //消費者線程  
  90. class Consumer implements Runnable  
  91. {  
  92.     private Resource res;  
  93.     Consumer(Resource res)  
  94.     {  
  95.         this.res=res;  
  96.     }  
  97.     //複寫run  
  98.     public void run()  
  99.     {  
  100.         while(true)  
  101.         {  
  102.             try  
  103.             {  
  104.                 res.getConsumer();  
  105.             }  
  106.             catch (InterruptedException e)  
  107.             {  
  108.             }  
  109.         }  
  110.     }  
  111.   
  112. }  
  113.   
  114. class  ProducerConsumer  
  115. {  
  116.     public static void main(String[] args)   
  117.     {  
  118.         Resource res=new Resource();  
  119.   
  120.         new Thread(new Producer(res)).start();//第一個生產線程 p1  
  121.         new Thread(new Consumer(res)).start();//第一個消費線程 c1  
  122.   
  123.         new Thread(new Producer(res)).start();//第二個生產線程 p2  
  124.         new Thread(new Consumer(res)).start();//第二個消費線程 c2  
  125.     }  
  126. }  

運行結果:部分截圖

       

 

八、停止線程

        JDK 1.5版本之前,有stop停止線程的方法,但升級之後,此方法已經過時。

那麼現在我們該如果停止線程呢?

        只有一種辦法,那就是讓run方法結束。

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

      如:run方法中有如下代碼,設置一個flag標記。  

[java] view plaincopy
  1. public  void run()  
  2. {  
  3.     while(flag)  
  4.     {     
  5.         System.out.println(Thread.currentThread().getName()+"....run");  
  6.     }  
  7. }  

        那麼只要在主函數或者其他線程中,在該線程執行一段時間後,將標記flag賦值false,該run方法就會結束,線程也就停止了。

2、上面的1方法可以解決一般情況,但是有一種特殊情況:就是當線程處於凍結狀態。就不會讀取到標記。那麼線程就不會結束。

        當沒有指定的方式讓凍結的線程恢復到運行狀態時,這時需要對凍結進行清除。強制讓線程恢復到運行狀態中來。這樣就可以操作標記讓線程結束。Thread類提供該方法interrupt();

如:

[java] view plaincopy
  1. class StopThread implements Runnable  
  2. {  
  3.     private boolean flag =true;  
  4.     public  void run()  
  5.     {  
  6.         while(flag)  
  7.         {  
  8.             System.out.println(Thread.currentThread().getName()+"....run");  
  9.         }  
  10.     }  
  11.     public void changeFlag()  
  12.     {  
  13.         flag = false;  
  14.     }  
  15. }  
  16.   
  17. class  StopThreadDemo  
  18. {  
  19.     public static void main(String[] args)   
  20.     {  
  21.         StopThread st = new StopThread();  
  22.         Thread t1 = new Thread(st);  
  23.         Thread t2 = new Thread(st);   
  24.         t1.start();  
  25.         t2.start();   
  26.   
  27.         int num = 0;  
  28.         while(true)  
  29.         {  
  30.             if(num++ == 60)  
  31.             {  
  32.                 t1.interrupt();//清除凍結狀態  
  33.                 t2.interrupt();  
  34.                 st.changeFlag();//改變循環標記  
  35.                 break;  
  36.             }  
  37.             System.out.println(Thread.currentThread().getName()+"......."+num);  
  38.         }  
  39.         System.out.println("over");  
  40.     }  
  41. }  

結果:

       

 

九、什麼時候寫多線程?

        當某些代碼需要同時被執行時,就用單獨的線程進行封裝。

示例:

[java] view plaincopy
  1. class  ThreadTest  
  2. {  
  3.     public static void main(String[] args)   
  4.     {  
  5.         //一條線程  
  6.         new Thread()  
  7.         {  
  8.             public void run()  
  9.             {  
  10.                 for (int x=0;x<100 ;x++ )  
  11.                 {  
  12.                     System.out.println(Thread.currentThread().toString()+"....."+x);  
  13.                 }  
  14.             }  
  15.         }.start();  
  16.       
  17.         //又是一條線程  
  18.         Runnable r= new Runnable()  
  19.         {  
  20.             public void run()  
  21.             {  
  22.                 for (int x=0;x<100 ;x++ )  
  23.                 {  
  24.                     System.out.println(Thread.currentThread().getName()+"....."+x);  
  25.                 }  
  26.             }  
  27.         };  
  28.         new Thread(r).start();  
  29.           
  30.         //可以看作主線程  
  31.         for (int x=0;x<1000 ;x++ )  
  32.         {  
  33.             System.out.println("Hello World!");  
  34.         }  
  35.           
  36.     }  
  37. }  

 


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