JAVA基礎學習之多線程

進程與線程

進程:就是正在進行的程序。其實就是一個應用程序運行時的內存空間。

線程:線程就是進程當中的一個控制單元或執行路徑。進程負責空間的標示,而線程負責執行應用程序的執行順序。

當一個進程中出現多個線程是就是多線程。每個線程在棧中都有自己的執行空間、方法區、變量。

java VM啓動的時候會有一個進程java.exe。該進程中至少有一個線程負責java程序的執行,而且這個線程運行的代碼存在於main方法中。該線程稱之爲主線程。

線程的創建方法

創建線程中第一種方式:繼承Thread類

步驟:

    1.定義類繼承Thread類

    2.複寫Thread類中run方法

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

    3.調用線程start方法

        該方法有兩個作用:啓動線程,調用run方法。

爲什麼覆蓋run方法?

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

也就是說Thread類中run方法,用於存儲線程要運行的代碼。

    線程都有自己的名稱:Thread-編號 該編號從0開始,。

    static Thread currentThread();獲取當前線程對象。

    getName()獲取線程名稱。

    設置線程名稱: setName()或者構造函數。

代碼實例:

class Demo extends Thread

{

    public void run()

    {

       for(int x=0; x<60; x++)

           System.out.println("demo run----"+x);

    }

}

class ThreadDemo

{

    public static void main(String[] args)

    {

       Demo d = new Demo();//創建好一個線程。

       d.start();//開啓線程並執行該線程的run方法。

       //d.run();//僅僅是對象調用方法。而線程創建了,並沒有運行。

       for(int x=0; x<60; x++)

           System.out.println("Hello World!--"+x);  

    }

}

第二種方式:實現Runable接口

步驟:

    1.定義類實現Runable接口

    2.覆蓋Runable接口中的run方法。

        將線程運行的代碼存儲在run方法中。

    3.通過Thread類建立線程對象。

    4.將Runable接口的子類對象作爲實際參數傳遞給Thread類的構造函數。Thread(Runable r)

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

    5.調用Thread類的start方法開啓線程(調用Runable接口子類中的run方法。)

售票實例:

class Ticket implements Runnable//extends Thread

{

    private  int tick = 100;

    public void run()

    {

       while(true)

       {

           if(tick>0)

           {

              System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);

           }

       }

    }

}

class  TicketDemo

{

    public static void main(String[] args)

    {

       Ticket t = new Ticket();

       Thread t1 = new Thread(t);//創建了一個線程;

       Thread t2 = new Thread(t);//創建了一個線程;

       Thread t3 = new Thread(t);//創建了一個線程;

       Thread t4 = new Thread(t);//創建了一個線程;

       t1.start();

       t2.start();

       t3.start();

       t4.start();

           }

}

兩種方式的區別:

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

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

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

線程狀態

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

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

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

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

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

多線程安全問題:一個線程在運行多條語句、操作同一個數據的時候,其他線程參與進來。導致錯誤數據的產生。

解決辦法:只要讓共享數據在某一時間只由一個線程執行,在此過程中其他的不能執行。所以會用到同步Sychronized(對象){需要被同步的代碼}。

   對象如同鎖,持有鎖的線程可以在同步中執行,沒有持有鎖的線程即使獲取CPU的執行權,也進不去,因爲沒有獲取鎖

同步函數:用synchronized修飾函數即可。

    鎖是this。若同步函數被static修飾,所屬類.class。

同步的前提

    必須有兩個或兩個以上線程才需要同步

    多個線程必須用同一個鎖。

同步的好處是解決的線程安全問題,弊端是需要不斷的判斷鎖,消耗資源。

多線程間通信和一些線程操作方法

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

需要使用的方法有:

wait();

notify();

notifyAll(); 

上面的方法都使用在同步中,因爲要對持有監視器(鎖)的線程操作,所以要使用在同步中,因爲只有同步才具有鎖 

爲什麼這些操作線程的方法要定義在Object類中呢?

因爲這些方法在操作同步中線程時,都必須要標識它們所操作線程持有的鎖,只有同一個鎖上的被等待線程,可以被同一個鎖上的notify喚醒,不可以對不同鎖中的線程進行喚醒,也就是說,等待和喚醒必須是同一個鎖,而鎖可以是任意對象,所以可以被任意對象調用的方法定義在Object類中。

同步中的死鎖問題

何時會出現死鎖?當同步中嵌套同步時會出現死鎖。 這時會出現一個線程持有a鎖要想執行下去要獲取b鎖,而另一個線程持有b鎖要想執行下去要獲取a鎖。這就成了兩個線程各自持有一個鎖,又要獲取對方的鎖,這時就出現了死鎖。程序會定在那執行不下去。

停止線程

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

特殊情況:

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

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