【重磅出擊】 java入門到精通——多線程(上)

來源:互聯網
上載者:User
文章目錄
  • 1.1進程和線程
Java 多線程(上)1 線程和進程的區別1.1進程和線程

          進程是指一個記憶體中啟動並執行應用程式,每個進程都有自己獨立的一塊記憶體空間,一個進程中可以有多個線程。比如在Windows系統中,一個啟動並執行xx.exe就是一個進程。Java程式的進程裡有幾個線程:主線程, 記憶體回收線程(後台線程)。

     線程是指進程中的一個執行任務(控制單元),一個進程中可以運行多個線程,多個線程可共用資料。多進程:作業系統中同時啟動並執行多個程式,在同一個進程中同時啟動並執行多個任務;一個進程至少有一個線程,為了提高效率,可以在一個進程中開啟多個控制單元。
 

     並發運行。如:多線程下載軟體。 

     多線程下載:此時線程可以理解為下載的通道,一個線程就是一個檔案的下載通道,多線程也就是同時開起好幾個下載通道.當伺服器提供下載服務時,使用下載者是共用頻寬的,在優先順序相同的情況下,總伺服器會對總下載線程進行平均分配。不難理解,如果你線程多的話,那下載的越快。現流行的下載軟體都支援多線程。可以完成同時運行,但是通過程式啟動並執行結果發現,雖然同時運行,但是每一次結果都不一致。
 

因為多線程存在一個特性:隨機性。  

造成的原因:CPU在瞬間不斷切換去處理各個線程而導致的。  

可以理解成多個線程在搶cpu資源。  

1.2 線程與進程的比較

     線程具有許多傳統進程所具有的特徵,故又稱為輕型進程或進程元;而把傳統的進程稱為重型進程,它相當於只有一個線程的任務。在引入了線程的作業系統中,通常一個進程都有若干個線程,至少需要一個線程。

進程與線程的區別:

     1.進程有獨立的進程空間,進程中的資料存放空間(堆空間和棧空間)是獨立的。

     2.線程的堆空間是共用的,棧空間是獨立的,線程消耗的資源也比進程小,相互之間可以影響的。

     

    如果想詳細瞭解線程與進程請看我的《線程與進程的詳細分析》。

2. 建立線程   2.1 建立線程第一種方式(繼承):

     1.  建立一個類,繼承Thread

     2.  複寫 run方法

     3.  建立一個線程對象

     4.  啟動線程(線程對象.start())

代碼如下:

class MyThread extends Thread{public void run() {for (int i = 0; i < 100; i++) {System.out.println("MyThread---->"+ i);}}}class ThreadDemo1 {public static void main(String[] args) {for (int i = 0; i < 100; i++) {System.out.println("main------>" +i);if(i == 10){new MyThread().start();}}    }}

   2.2 建立線程第二種方式(實現):

      實現Runnable介面
     1.  子類覆蓋介面中的run方法。
     2.  通過Thread類建立線程,並將實現了Runnable介面的子類對象作為參數傳遞給Thread類的建構函式。
     3.  Thread類對象調用start方法開啟線程。
代碼如下:

class MyThread2 implements Runnable {public void run() {// 線程體for (int i = 0; i < 100; i++) {System.out.println("MyThread2----->" + i);}}}public class ThreadDemo2 {public static void main(String[] args) {for (int i = 0; i < 100; i++) {System.out.println("main-->" +i);if (i == 10) {new Thread(new MyThread2()).start();}}}}

         現在我們通過一個經典案例來說明這兩種方式的區別:

售票的例子,這個例子基本上每一本java入門書上都會有!

需求:有50張票需要3個售票視窗賣出;用兩種開啟線程方式買票,觀察兩種方式買票的結果有什麼不同?

   1.  繼承Thread方式

class Ticket1 extends Thread{int num = 20;public Ticket1(String name){super(name);}public void run() {for (int i = 0; i < 100; i++) {if(num >0) {System.out.println(getName()+"賣出第" +num-- +"張");}}}}public class TicketDemo {public static void main(String[] args) {//3個視窗買new Ticket1("售票員-1").start();new Ticket1("售票員-2").start();new Ticket1("售票員-3").start();}  }

輸出結果的一部分:

 售票員-1賣出第50張

  售票員-3賣出第50張

  售票員-2賣出第50張

  售票員-3賣出第49張

  售票員-1賣出第49張

  售票員-3賣出第48張


  售票員-3賣出第47張


   2.  實現Runnable方式

class Ticket2 extends Object implements Runnable{int num = 20;public void run() {for (int i = 0; i < 50; i++) {if(num >0) {System.out.println(Thread.currentThread().getName()+"賣出第" +num-- +"張");
}}}}public class TicketDemo2 {public static void main(String[] args) {Runnable target = new Ticket2();new Thread(target,"售票員-1").start();new Thread(target,"售票員-2").start();new Thread(target,"售票員-3").start();}}

輸出結果的一部分:

    

   售票員-1賣出第50張

   售票員-3賣出第48張

   售票員-2賣出第49張

   售票員-3賣出第46張

   售票員-2賣出第45張

   售票員-2賣出第43張


currentThread():返回對當前正在執行的線程對象的引用。

getName():擷取線程名稱。

setName()設定線程名字。

將兩種方法的輸出結果進行比較,我們會發現

     繼承Thread類的輸出結果一共列印了150條,說明一條票被賣了三次,這顯然是不正確的。

   而實現Runnable介面卻沒有出現這樣的問題。

   

解釋:

    因為一個線程只能啟動一次,通過Thread實現線程時,線程和線程所要執行的任務是捆綁在一起的。也就使得一個任務只能啟動一個線程,不同的線程執行的任務是不相同的,所以沒有必要,也不能讓兩個線程共用彼此任務中的資源。

    一個任務可以啟動多個線程,通過Runnable方式實現的線程,實際是開闢一個線程,將任務傳遞進去,由此線程執行。可以執行個體化多個 Thread對象,將同一任務傳遞進去,也就是一個任務可以啟動多個線程來執行它。這些線程執行的是同一個任務,所以他們的資源是共用。

兩種不同的線程實現方式本身就決定了其是否能進行資源共用

繼承Thread

同份資源不共用並且由於java的單繼承,程式以後不便於擴充。

實現Runnable:(推薦)

多個線程共用一個目標資源,適合多執行緒同一份資源。

該類還可以繼承其他類,也可以實現其他介面。


3 線程的生命週期

   

3.1 線程的生命週期之建立和就緒狀態

建立:當程式使用new建立一個線程後,該線程處於建立狀態,此時他和其他java對象一樣,僅僅由Java虛擬機器為其分配記憶體並初始化成員變數值。【 Thread r = new Thread() 】

就緒:當線程對象調用start()方法後,該線程處於就緒狀態,線程計入線程隊列排隊,此時該狀態線程並未開始執行,它僅表示可以運行了。至於該線程何時運行,取決於JVM線程調度器的調度。【 r.start() 】

3.2 線程的生命週期之運行和阻塞狀態

運行:若處於就緒狀態的線程獲得了CPU,開始執行run()線程執行體,該線程處於執行狀態。

阻塞:線程運行過程中需要被中斷,目的是是其他的線程獲得執行的機會。該狀態就會進入阻塞狀態。

注意:阻塞狀態不能直接轉成運行狀態,阻塞狀態只能重新進入就緒狀態。

3.3 線程的生命週期之死亡

run()執行完成,線程正常結束; 線程拋出未捕獲的Exception或Error;

調用線程的stop()。(易導致死結,不推薦)

注意:

主線程結束後,其他線程不受其影響,不會隨之結束;

一旦子線程啟動起來後,就擁有和主線程相等地位,不受主線程影響。

測試線程是否活著,可用線程對象的isAlive()方法。當線程處於就緒,運行,阻塞狀態返回true。當線程處於建立和死亡狀態,返回false。

已死亡的線程是不可以通過start()方法喚醒線程的,否則引發IllegalThreadStateException異常;

       樓豬不是什麼大牛,但自我評價覺得還總結的不錯,挺適合新手。 如果發現哪裡錯了,望各位大牛指點!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.