1. 程式人生 > >多執行緒詳解(二)

多執行緒詳解(二)

多執行緒詳解(二)

在正式介紹執行緒建立的第二種方法之前,我們接著多執行緒詳解(一),講一下:對執行緒的記憶體圖、執行緒的狀態,為下面的學習打下基礎,小夥伴們不要急喲!!

一、多執行緒執行的記憶體圖(ps.博主沒有找到合適的畫圖工具,歡迎大神們貢獻啊)

class person extends Thread
{   
    int i;
    private String name;
    person(String name)
    {
//給執行緒命名,使用super
        super(name);
        this.name=name;
    }
    public
void run() { show(); } public void show() { for(i=0;i<=10;i++) { System.out.println(name+"x="+i+"...name="+Thread.currentThread().getName()); } } } public class demo1{ public static void main(String[] args){ person p1=new person("zhangsan"
); person p2=new person("lisi"); p1.start(); p2.start(); System.out.println("出來吧,name="+Thread.currentThread().getName()); } }

每開啟一條執行緒就多一條執行路徑,所以以上述程式碼為例——共有三條路徑

    (1)main執行緒路徑
    (2)run執行緒路徑
    (3)另一條run執行緒路徑

1、每一條路徑上獨立的進行壓棧彈棧,每一條執行緒上都要自己的棧區,所以互相不影響

2、當一條路勁上的程式碼執行完,該執行緒自動結束

3、當main執行緒提前執行結束後,其他兩條執行緒依舊執行。所以可以看出每條執行緒之間是完全獨立的,當一條執行緒有異常時,其他的執行緒繼續執行不受影響。

4、為多執行緒安全分析埋下伏筆(這裡不詳細講多執行緒安全了,以後會更行的哦)

二、執行緒的狀態(重點)

1、執行(使用start方法):具備執行資格和執行權

2、消亡(任務執行完了,自動消亡OR使用stop方法強制消亡):既不具備執行資格也不具備執行資格

3、凍結:

《1》使用sleep(time)方法———時間到了,自動恢復————既不擁有執行權也不擁有執行資格(釋放執行權的同時釋放執行資格)

《2》使用wait()方法————使用notify()方法喚醒————既不擁有執行權也不擁有執行資格(釋放執行權的同時釋放執行資格))

解釋:如果一個執行緒處於執行狀態,那麼CPU就具備執行資格和擁有CPU的執行權,那麼什麼是執行權?什麼又是執行資格呢??

《1》CPU執行資格:CPU可以處理,在CPU的佇列中排隊等待處理

《2》CPU執行權:正在被CPU處理

下面我們通過舉一個例子,解釋一下什麼是臨時阻塞

假設A、B、C、D四個執行緒都處於執行狀態,當A執行的時候,A具備執行資格和執行權,此時其他三個具備執行資格但正在等待執行權,這種狀態就是臨時阻塞狀態

所以,臨時阻塞狀態———具備執行資格但是不具備執行權

在一個時刻,只有一個執行緒擁有執行權,而其他執行的執行緒都是臨時阻塞的。

辨析:臨時阻塞狀態和凍結狀態的區別
    (1)執行資格和執行權的不同
    (2)凍結是由程式設計師控制的而臨時阻塞是cpu執行控制的
    (3)臨時阻塞的時間往往是極短的,但是凍結的時間是自定義的,相比較而言是長的。
    (4)臨時阻塞是執行時的一種狀態,所以凍結喚醒後,必須經過執行才能有時間阻塞。

三、建立執行緒的第二種方式

1、繼承Thread類建立方法的侷限性

如果原來的類已經繼承了其他類,使用第一種方式繼承Thread,就存在了多繼承,在java中是不允許的

例如:

class person extends fu
{   int i;
    private String name;
    person(String name)
    {   
        super(name);
        this.name=name;
    }
    public void run()
    {
        show();
    }

    public void show()
    {
        for(i=0;i<=10;i++)
        {
            System.out.println(name+"x="+i+"...name="+Thread.currentThread().getName());
        }
    }
}
public class demo1{
public static void main(String[] args){
    person p1=new person("zhangsan");
    person p2=new person("lisi");
    p1.start();
    p2.start();
    System.out.println("出來吧,name="+Thread.currentThread().getName());
}
}

但是在這種情況下,如果你依舊要使用繼承Thread的方法建立執行緒也是可以的,就是利用再增加一層繼承的方法來實現。示例如下:

class fu extends Thread
{
     int i;
    private String name;
    person(String name)
    {
        super(name);
        this.name=name;
    }
    public void run()
    {
        show();
    }

    public void show()
    {
        for(i=0;i<=10;i++)
        {
            System.out.println(name+"x="+i+"...name="+Thread.currentThread().getName());
        }
    }
}
class person extends fu
{      
}
public class demo1{
public static void main(String[] args){
    person p1=new person("zhangsan");
    person p2=new person("lisi");
    p1.start();
    p2.start();
    System.out.println("出來吧,name="+Thread.currentThread().getName());
}
}

從上面的示例我們可以看出,多增加一層繼承關係,其實是開發者強制加上去的,而且實現過程也很繁瑣,所以我們應該拋棄此方法,重新思考……..

2、使用介面的方法建立執行緒

我們現在的目的是讓person類建立一個執行緒,但是person類不可以再繼承。由於需要擴充套件person類功能,所有我們很自然的想到使用————介面

建立步驟:

    《1》定義類實現Runnable介面
    《2》覆蓋介面中的run方法,將執行緒的任封裝到run方法中
    《3》通過Thread類建立執行緒物件,並將Runnable介面的子類物件作為Thread類的建構函式進行傳遞。
    《4》呼叫執行緒物件的start方法開啟執行緒

為什麼第三步中,要將Runnable介面的子類物件作為Thread類的建構函式進行傳遞呢?

因為,執行緒的任務都是封裝在Runnable介面子類物件的run方法中。所以要線上程物件建立時就明確要執行的任務。

示例程式碼如下:

//實現Runable介面,
class person implements Runnable
{   int i;
    private String name;
    person(String name)
    {
        this.name=name;
    }
//  覆蓋run方法
    public void run()
    {
        show();
    }

    public void show()
    {
        for(i=0;i<=10;i++)
        {
            System.out.println(name+"x="+i+"...name="+Thread.currentThread().getName());
        }
    }
}
public class demo1{
public static void main(String[] args){
//這個不是執行緒物件
//  person p1=new person("zhangsan");
//  person p2=new person("lisi");
//這個是執行緒物件
    person p=new person("zhangsan");

    Thread t1=new Thread(p);
    Thread t2=new Thread(p);
    t1.start();
    t2.start();
    System.out.println("出來吧,name="+Thread.currentThread().getName());
}
}

執行結果:

這裡寫圖片描述

關於多執行緒建立的第二種方法的詳細介紹,請閱讀多執行緒詳解(三)