1. 程式人生 > >別翻了,這篇文章就是要讓你入門java多執行緒!

別翻了,這篇文章就是要讓你入門java多執行緒!

目錄

  • 1. 理解執行緒與程序
  • 2、理解並行和併發
  • 3、特殊的一個單執行緒:主執行緒(Main執行緒)
  • 4、 建立多執行緒的四種方式
  • 5、使用匿名內部類方式建立執行緒
  • 6、執行緒安全問題
  • 7、解決執行緒安全問題:執行緒同步
  • 8、執行緒常用方法
  • 9、執行緒的狀態
  • 10、執行緒池


就在前幾天,有位讀者朋友私信宜春,說期待出一篇多執行緒的文章,我當時內心是小鹿亂撞啊....於是這幾天茶不思飯不想,好幾天深夜皆是輾轉反側,兩目深凝,以至於這幾天走起路來格外飄飄然,左搖右晃的,魔鬼般的步伐,一般兩步,走在大馬路中央上差點被打~我承認太誇張了,感覺又要被打~。最終還是君意不可違,答應了這位讀者朋友,從這位讀者朋友的部落格頭像可以看的出來,這位朋友絕bi歷經滄桑,對生活無盡的坦然浩對,看透俗世凡塵、世態炎涼、趨炎附勢,擁有著極高的安心恬蕩情懷...啥?啥子?這個是系統預設頭像....嗯嗯嗯呃。。。那個那個宜春啥都沒說哈,別把什麼事都扯宜春身上,你們一天天的,我啥都沒說(理直氣壯)...

@

1. 理解執行緒與程序

由於併發肯定涉及到多執行緒,因此在進入併發程式設計主題之前,我們先來了解一下程序和執行緒的由來,這對後面對併發程式設計的理解將會有很大的幫助。

程序和執行緒的對比這一知識點由於過於基礎,正因為過於基礎,所以我們更應該透徹它!我們必須掌握什麼是執行緒和程序,掌握執行緒與程序的關係、區別及優缺點 !

1.1、何為程序?

首先我們來看一下程序的概念:

程序:是指一個記憶體中執行的應用程式,每個程序都有一個獨立的記憶體空間,一個應用程式可以同時執行多個程序;程序也是程式的一次執行過程,是系統執行程式的基本單位;系統執行一個程式即是一個程序從建立、執行到消亡的過程。

看完之後,是不是感覺很抽象?很懵bi?懵bi就對了,說明你和我智商一樣高....~開個玩笑~

不妨先憋棄上面的概念,放鬆一下大腦,雙擊開啟LOL,秒選德馬打野,輸了直接退出遊戲並且保持微笑,然後正襟危坐心平氣和的看宜春寫的部落格....

這個時候的你不僅僅是愉快的擼了一把遊戲,而且還親自體驗擼了一把程序...其實在你雙擊開啟LOL的時候就已經建立了程序,此話怎講?眾所周知,我們的電腦安裝的軟體比如:LOL、微信、谷歌等等都是儲存在我們的硬碟上的,硬碟上的資料可以說是永久儲存(ORM),當我們雙擊LOL的時候,LOL程式執行就進入了記憶體中,所有的程式必須進入記憶體中才能執行,記憶體屬於臨時儲存(RAM),而進入記憶體的程式都可以叫做是程序,把LOL程式退出的時候,LOL程式就會退出記憶體,程序也就隨之銷燬了!因此說各位擼了一把程序也不為過吧。

啥?字太多了,看的不夠明瞭,不如看圖得勁....額。。。

上面主要是通過抽象的描述了程序,其實程序是可以很直觀的看的到的,我們可以再電腦底部工作列,右鍵----->開啟工作管理員,可以檢視當前任務的程序:

其實,關於執行緒博主我完全可以一兩句話概括,但是這樣並不負責,畢竟這篇文章標題就是要讓你徹底入門java多執行緒。如果連程序都理解不好談何徹底理解多執行緒?

1.2、何為執行緒?

同樣的,我們先來看執行緒的概念

執行緒是程序中的一個執行單位,負責當前程序中程式的執行。一個程序中至少有一個執行緒,也就是說一個程序可以有多個執行緒的,而多個執行緒的程序運用程式就叫做多執行緒程式

執行緒的概念稍微好理解很多,但是想更深層次的去理解光靠上面一段文字的概述是完全不夠的!

這不打LOL的過程中,屬實卡的一批,果然花高價998買的6手戴爾筆記本打LOL屬實像極了愛情。這個時候不得不雙擊開啟電腦安全管家進行防毒,果然2500天沒有進行過病毒查殺,我天。。。其實我相信很多人都用過電腦管家或者手機管家之類的安全軟體,我們都很清楚我們開啟病毒查殺之後一般要幾分鐘掃描查殺,這個時候我們是可以讓它後臺進行的,我們不會等而是開啟另一個垃圾清理的功能,這個時候我們也不會等而是再去啟動電腦加速功能。等到 這些操作都完成之後果斷退出電腦管家,繼續LOL,果然高價998買的6手戴爾筆記本再怎麼防毒打LOL還是照樣的卡....

其實清楚執行緒必然涉及到CPU的相關概念了,將上面文字所描述的用圖片概括,大致為:

1.3、何為多執行緒?

從上一節中,我們也提到過多執行緒,所以理解起來應該不難。

多執行緒就是多個執行緒同時執行 或 交替執行。

單核CPU:交替執行。
多核CPU:同時執行。

其實,多執行緒程式並不能提高程式的執行速度,但能夠提高程式執行效率,讓CPU的使用率更高。

1.4、何為執行緒排程優先順序?

說起執行緒排程優先順序這個概念,就讓我想到現在我們大部分人投簡歷一樣。如果你的學歷或者工作經驗越高,那麼你的優先順序就越高,面試官很大機率就會讓你去面試但也不是一定只是機率特別大,如果執行緒的優先順序相同,那麼會隨機選擇一個(執行緒隨機性)!在我們每個人的電腦中執行緒是可以設定執行緒的優先順序的,但是生活中沒有優先順序(學歷、工作經驗)的孩子就只能靠自己的能力了~媽耶,太真實了...~

執行緒優先順序具有繼承特性比如A執行緒啟動B執行緒,則B執行緒的優先順序和A是一樣的。

執行緒優先順序具有隨機性也就是說執行緒優先順序高的不一定每一次都先執行完,只是被執行的可能性更大。

在今後的多執行緒學習旅遊中我們會使用到getPriority()方法獲取執行緒的優先順序。

1.5、為什麼提倡使用多執行緒而不是多程序?

執行緒與程序相似,但執行緒是一個比程序更小的執行單位,是程式執行的最小單位。一個程序在其執行的過程中可以產生多個執行緒。與程序不同的是同類的多個執行緒共享同一塊記憶體空間和一組系統資源,所以系統在產生一個執行緒,或是在各個執行緒之間作切換工作時,負擔要比程序小得多,也正因為如此,執行緒也被稱為輕量級程序。同時執行緒是程式執行的最小單位。使用多執行緒而不是用多程序去進行併發程式的設計,是因為執行緒間的切換和排程的成本遠遠小於程序。

而使用多執行緒,多執行緒會將程式執行方式從序列執行變為併發執行,效率會有很大提高。

2、理解並行和併發

在博主認為併發和並行是兩個非常容易被混淆的概念。為了防止繞暈大家,所以我選擇長話短說!

  1. 併發:一個時間段內同時發生(並不是同時發生)。
  2. 並行:同一時刻發生(真正的同時發生)。

它們都可以表示兩個或者多個任務一起執行,但是偏重點有些不同。

於此同時,我們不妨回顧一下上面所提到過的CPU,並再次理解併發與並行的區別,從而溫故知新 ~我TM簡直是個天才!~

單核CPU:交替執行【併發】
多核CPU:同時執行【並行】

併發給人的感覺是同時執行,那是因為分時交替執行的時間是非常短的!

3、特殊的一個單執行緒:主執行緒(Main執行緒)

我們常說的主執行緒就是Main執行緒,它是一個特殊的單執行緒,話不多說,直接擼碼:

定義一個用於測試的demo類Person

package demo;

public class Person {
   public String name;

   public Person(String name){
       this.name=name;
   }

   public void run(){
       int i=1;
       while (i<5){
           System.out.println(name+i);
           i++;
       }
   }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

編寫Main方法

package demo;

public class MainThreadDemo {
    public static void main(String[] args) {
        Person per=new Person("常威");
        per.run();

        Person Per2=new Person("來福");
        Per2.run();
    }
}

執行結果就已經很顯而易見了,放心我不是靠你們執行結果而是單純的先分析主執行緒。

執行結果:
    常威1
    常威2
    常威3
    常威4
    來福1
    來福2
    來福3
    來福4

3.1、分析主執行緒原理

3.2、 單執行緒的侷限性

單執行緒不僅效率低下,而且存在很大的侷限性,惟一的優點就是安全。所以說女孩子長得安全其實也是一種優點,噗哈哈哈...

如何體現出單執行緒效率低下以及它的侷限性呢?其實只要一句程式碼即可,還是以上面的單執行緒Main執行緒為例:

package demo;

public class MainThreadDemo {
    public static void main(String[] args) {
        Person per=new Person("常威");
        per.run();
        int a=6/0;  //=====================特別注意這行程式碼
        Person Per2=new Person("來福");
        Per2.run();
    }
}

試想一下執行結果...

如果對上面的執行結果有問題,或者疑問。那沒錯了,你簡直是個天(小)才(白)!真真的天(小)才(白),很有可能異常機制沒學好,好吧我給你貼出來:【java基礎之異常】死了都要try,不淋漓盡致地catch我不痛快!

言歸正傳,效率低下何以見得?這是資料少,如果是一億條資料呢,單執行緒就是一個一個列印。那侷限性又何以見得呢?從上面執行結果來看也能看出,只因為一行程式碼而導致下面程式碼不再執行。已經很明顯了。

4、 建立多執行緒的四種方式

說是說建立多執行緒有四種方式,但考慮到是入門文章還是主要寫入門的兩種方式,剩下的兩個暫時忽略。忽略的兩種方法有:實現Callable介面通過FutureTask包裝器來建立Thread執行緒、使用ExecutorServiceCallableFuture實現有返回結果的執行緒。現在可能對於入門的童鞋來說是接收不了的,以後再去了解也不晚!

4.1、繼承Thread類

Java使用java.lang.Thread類代表執行緒,所有的執行緒物件都必須是Thread類或其子類的例項。每個執行緒的作用是完成一定的任務,實際上就是執行一段程式流即一段順序執行的程式碼。Java使用執行緒執行體來代表這段程式流。

Java中通過繼承Thread類來建立並啟動多執行緒的步驟如下:

  1. 定義Thread類的子類,並重寫該類的run()方法,該run()方法的方法體就代表了執行緒需要完成的任務,因此把run()方法稱為執行緒執行體。
  2. 建立Thread子類的例項,即建立了執行緒物件
  3. 呼叫執行緒物件的start()方法來啟動該執行緒

程式碼如下:

測試類:

public class Demo01 {
    public static void main(String[] args) {
        //建立自定義執行緒物件
        MyThread mt = new MyThread("新的執行緒!");
        //開啟新執行緒
        mt.start();
        //在主方法中執行for迴圈
        for (int i = 0; i < 10; i++) {
            System.out.println("main執行緒!"+i);
        }
    }
}

自定義執行緒類:

public class MyThread extends Thread {
    //定義指定執行緒名稱的構造方法
    public MyThread(String name) {
        //呼叫父類的String引數的構造方法,指定執行緒的名稱
        super(name);
    }
    /**
     * 重寫run方法,完成該執行緒執行的邏輯
     */
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName()+":正在執行!"+i);
        }
    }
}

Thread類本質上是實現了Runnable介面的一個例項,代表一個執行緒的例項。啟動執行緒的唯一方法就是通過Thread類的start()例項方法。start()方法是一個native方法,它將啟動一個新執行緒,並執行run()方法。這種方式實現多執行緒很簡單,通過自己的類直接extend Thread,並複寫run()方法,就可以啟動新執行緒並執行自己定義的run()方法。

4.2、實現Runnable介面

如果自己的類已經繼承另一個類,就無法直接繼承Thread,此時,可以實現一個Runnable介面來建立執行緒,顯然實現Runnable介面方式建立執行緒的優勢就很明顯了。

直接擼碼:

自定義一個類實現Runnable介面,並重寫介面中的run()方法,併為run方法新增要執行的程式碼方法。

public class RunableDemo implements Runnable{

    @Override
    public void run() {
        int a = 1;
        while (a<20){
            System.out.println(Thread.currentThread().getName()+ a);//Thread.currentThread().getName()為獲取當前執行緒的名字
            a++;
        }
    }
}

編寫Main方法

為了啟動自定義類RunableDemo ,需要首先例項化一個Thread,並傳入RunableDemo 例項:

public class MainThreadDemo {

    public static void main(String[] args) {
        RunableDemo runn=new RunableDemo();
        
        //例項化一個Thread並傳入自己的RunableDemo 例項
        Thread thread=new Thread(runn);
        thread.start();

        int a = 1;
        while (a<20){
            //Thread.currentThread().getName()為獲取當前執行緒的名字
            System.out.println(Thread.currentThread().getName()+ a);
            a++;
        }
    }
}

執行結果:

main1
main2
main3
Thread-01
Thread-02
Thread-03
Thread-04
Thread-05
Thread-06
....

其實多執行幾遍,你會方法每次執行的結果順序都不一樣,這主要是由於多執行緒會去搶佔CPU的資源,誰搶到了誰就執行,而Main和Thread兩個執行緒一直在爭搶。

實際上,當傳入一個Runnable target(目標)引數給Thread後,Threadrun()方法就會呼叫target.run(),參考JDK原始碼:

public void run() {  
  if (target != null) {  
   target.run();  
  }  
}  

4.3、兩種入門級建立執行緒的區別

採用繼承Thread類方式:

(1)優點:編寫簡單,如果需要訪問當前執行緒,無需使用Thread.currentThread()方法,直接使用this,即可獲得當前執行緒。
(2)缺點:因為執行緒類已經繼承了Thread類,所以不能再繼承其他的父類。

採用實現Runnable介面方式:

(1)優點:執行緒類只是實現了Runable介面,還可以繼承其他的類。在這種方式下,可以多個執行緒共享同一個目標物件,所以非常適合多個相
同線程來處理同一份資源的情況,從而可以將CPU程式碼和資料分開,形成清晰的模型,較好地體現了面向物件的思想。
(2)缺點:程式設計稍微複雜,如果需要訪問當前執行緒,必須使用Thread.currentThread()方法。

小結:
如果一個類繼承Thread,則不適合資源共享。但是如果實現了Runable介面的話,則很容易的實現資源共享。

實現Runnable介面比繼承Thread類的優勢:

1.適合多個相同程式碼的執行緒去處理同一個資源。

2.可以避免java中單繼承的限制。

3.增加程式碼的健壯性,實現解耦。程式碼可以被多個執行緒共享,程式碼和資料獨立。

4.執行緒池中只能放入實現Runnable或Callable類執行緒,不能放入繼承Thread的類【執行緒池概念之後會慢慢涉及】

所以,如果選擇哪種方式,儘量選擇實現Runnable介面!

其實學到後面的執行緒池,你會發現上面兩種建立執行緒的方法實際上很少使用,一般都是用執行緒池的方式比較多一點。使用執行緒池的方式也是最推薦的一種方式,另外,《阿里巴巴Java開發手冊》在第一章第六節併發處理這一部分也強調到“執行緒資源必須通過執行緒池提供,不允許在應用中自行顯示建立執行緒”。不過處於入門階段的童鞋博主還是強烈建議一步一個腳印比較好!

5、使用匿名內部類方式建立執行緒

談起匿名內部類,可能很多小白是比較陌生的,畢竟開發中使用的還是比較少,但是同樣是非常重要的一個知識!於此同時我就貼出關於匿名內部類的文章程式設計師你真的理解匿名內部類嗎?如果小白童鞋能看懂下面這個程式碼,真的你不需要看那篇文章了,你T喵的簡直是個天才!

package AnonymousInner;

public class NiMingInnerClassThread {
    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i<5;i++){
                    System.out.println("熊孩子:"+i);
                }
            }
        };
        new Thread(r).start();
        for (int i = 0; i < 5 ; i++){
            System.out.println("傻狍子:"+i);
        }
    }
}

小白童鞋還愣著幹啥呀趕緊去補補...

6、執行緒安全問題

執行緒安全問題主要是共享資源競爭的問題,也就是在多個執行緒情況下,一個或多個執行緒同時搶佔同一資源導致出現的一些不必要的問題,最典型的例子就是火車四個視窗售票問題了,這裡就不再舉售票例子了,已經爛大街了,這裡就簡單實現一個執行緒安全問題程式碼....

實現Runnable介面方式為例,主要實現過程是:例項化三個Thread,並傳入同一個RunableDemo 例項作為引數,最後開啟三條相同引數的執行緒,程式碼如下:

public class RunableDemo implements Runnable{
    public int a = 100;//執行緒共享資料
    
    @Override
    public void run() {
        while (a>0){
            System.out.println("執行緒"+Thread.currentThread().getName()+"執行到"+ a);
            a--;
        }
    }
}
public class MainThreadDemo {

    public static void main(String[] args) {
        RunableDemo runn=new RunableDemo();
        
        Thread thread1=new Thread(runn);
        Thread thread2=new Thread(runn);
        Thread thread3=new Thread(runn);
        thread1.start();
        thread2.start();
        thread3.start();
        }
 }

執行結果:

Thread-0==100
Thread-0==99
Thread-1==100
Thread-1==97
Thread-1==96
Thread-1==95
Thread-2==98
...

根據結果可以看出,確實是三條執行緒(Thread-0、1、2)在執行,安全問題就出線上程會出現相同的結果比如上面的100就出現了兩次,如果迴圈條件更改一下可能也會出現負數的情況。這種情況該怎麼解決呢?這個時候就需要執行緒同步了!

7、解決執行緒安全問題:執行緒同步

實際上,執行緒安全問題的解決方法有三種:

1、同步程式碼塊
2、同步方法
3、鎖機制

7.1、 synchronized同步程式碼塊

第一種方法:同步程式碼塊

格式:

synchronized(鎖物件) {
可能會出現執行緒安全問題的程式碼(訪問共享資料的程式碼)
}

使用同步程式碼塊特別注意:
1、通過程式碼塊的鎖物件,可以是任意物件
2、必須保證多個執行緒使用的鎖物件必須是同一個
3、鎖物件的作用是把同步程式碼快鎖住,只允許一個執行緒在同步程式碼塊執行

還是以上面執行緒安全問題為例子,使用同步程式碼塊舉例:

public class RunableDemo implements Runnable{
    public int a = 100;//執行緒共享資料

    Object object=new Object(); //事先準備好一個鎖物件

    @Override
    public void run() {
        synchronized (object){  //使用同步程式碼塊
        while (a>0){
            System.out.println("執行緒"+Thread.currentThread().getName()+"執行到"+ a);
            a--;
        }
        }
    }
}

Main方法沒有任何改動,執行一下結果是絕對沒問題的,資料都是正確的沒有出現重複情況這一出,各位可以自己嘗試一下!

同步程式碼塊的原理:

使用了一個鎖物件,叫同步鎖,物件鎖,也叫同步監視器,當開啟多個執行緒的時候,多個執行緒就開始搶奪CPU的執行權,比如現在t0執行緒首先的到執行,就會開始執行run方法,遇到同步程式碼快,首先檢查是否有鎖物件,發現有,則獲取該鎖物件,執行同步程式碼塊中的程式碼。之後當CUP切換執行緒時,比如t1得到執行,也開始執行run方法,但是遇到同步程式碼塊檢查是否有鎖物件時發現沒有鎖物件,t1便被阻塞,等待t0執行完畢同步程式碼塊,釋放鎖物件,t1才可以獲取從而進入同步程式碼塊執行。
同步中的執行緒,沒有執行完畢是不會釋放鎖的,這樣便實現了執行緒對臨界區的互斥訪問,保證了共享資料安全。
缺點:頻繁的獲取釋放鎖物件,降低程式效率

7.2、同步方法

使用步驟:

1、把訪問了共享資料的程式碼抽取出來,放到一個方法中
2、在該方法上新增 synchronized 修飾符

格式:

修飾符 synchronized 返回值型別 方法名稱(引數列表) {
  方法體...
}

程式碼示例:

public class RunableDemo implements Runnable{
    public int a = 100;//執行緒共享資料

    @Override
    public void run() {
        while (true){
            sell(); //呼叫下面的sell方法
        }
    }
    
    //訪問了共享資料的程式碼抽取出來,放到一個方法sell中 
    public synchronized void sell(){
        while (a>0){
            System.out.println("執行緒"+Thread.currentThread().getName()+"執行到"+ a);
            a--;
        }
    }
}

同步方法的也是一樣鎖住同步的程式碼,但是鎖物件的是Runable實現類物件,也就是this,誰呼叫方法,就是誰。

說到同步方法,就不得不說一下靜態同步方法,顧名思義,就是在同步方法上加上static,靜態的同步方法,新增一個靜態static修飾符,此時鎖物件就不是this了,靜態同步方法的鎖物件是本類的class屬性,class檔案物件(反射)

public class RunableDemo implements Runnable{
    public static int a = 100;//執行緒共享資料     =====此時共享資料也要加上static

    @Override
    public void run() {
        while (true){
            sell();
        }
    }

    public static synchronized void sell(){  //注意添加了static關鍵字
        while (a>0){
            System.out.println("執行緒"+Thread.currentThread().getName()+"執行到"+ a);
            a--;
        }
    }
}

使用靜態同步方法時,此時共享資料也要加上static,因為static成員才能訪問static成員,如果對static關鍵字不是他別理解的可以補補了,放心,博主有信心讓你有所收穫,會讓你重新認識到static的魅力:深入理解static關鍵字

當然靜態同步方法瞭解即可!

7.3、Lock鎖

Lock介面位於java.util.concurrent.locks.Lock它是JDK1.5之後出現的,Lock介面中的方法:

void lock(): 獲取鎖

 

void unlock(): 釋放鎖

Lock介面的一個實現類java.util.concurrent.locks.ReentrantLock implements Lock介面

使用方法:
1、在Runable實現類的成員變數建立一個ReentrantLock物件
2、在可能產生執行緒安全問題的程式碼前該物件呼叫lock方法獲取鎖
3、在可能產生執行緒安全問題的程式碼後該物件呼叫unlock方法釋放鎖

程式碼示例:

import java.util.concurrent.locks.ReentrantLock;

public class RunableDemo implements Runnable{
    public static int a = 100;//執行緒共享資料

    //1、在Runable實現類的成員變數建立一個ReentrantLock物件============
    ReentrantLock reentrantLock = new ReentrantLock();

    @Override
    public void run() {
        // 2、在可能產生執行緒安全問題的程式碼前該物件呼叫lock方法獲取鎖=======
        reentrantLock.lock();
        while (a>0){
            System.out.println("執行緒"+Thread.currentThread().getName()+"執行到"+ a);
            a--;
        }
        // 3、在可能產生執行緒安全問題的程式碼後該物件呼叫unlock方法獲取鎖======
        reentrantLock.unlock();
    }

}

當然更安全的寫法是,線上程安全問題程式碼中try...catchy,最後在finally語句中新增reentrantLock.unlock();,這樣方為上上策!

7.4、三種方法小結

第一種
synchronized 同步程式碼塊:可以是任意的物件必須保證多個執行緒使用的鎖物件是同一個

 

第二種
synchronized 同步方法: 鎖物件是this,誰呼叫鎖物件就是誰

 

synchronized 靜態同步方法: 鎖物件是其class物件,該物件可以用this.getClass()方法獲取,也可以使用當前類名.class 表示。【瞭解即可】

 

第三種
Look鎖方法:該方法提供的方法遠遠多於synchronized方式,主要在Runable實現類的成員變數建立一個ReentrantLock物件,並使用該物件呼叫lock方法獲取鎖以及unlock方法釋放鎖!

8、執行緒常用方法

8.1、Thread類

  Thread():用於構造一個新的Thread。

  Thread(Runnable target):用於構造一個新的Thread,該執行緒使用了指定target的run方法。

  Thread(ThreadGroup group,Runnable target):用於在指定的執行緒組中構造一個新的Thread,該

  執行緒使用了指定target的run方法。

  currentThread():獲得當前執行執行緒的物件引用。

  interrupt():將當前執行緒置為中斷狀態。

  sleep(long millis):使當前執行的執行緒進入睡眠狀態,睡眠時間至少為指定毫秒數。

  join():等待這個執行緒結束,即在一個執行緒中呼叫other.join(),將等待other執行緒結束後才繼續本執行緒。

  yield():當前執行的執行緒讓出CPU的使用權,從執行狀態進入就緒狀態,讓其他就緒執行緒執行。

8.2、Object類

  wait():讓當前執行緒進入等待阻塞狀態,直到其他執行緒呼叫了此物件的notify()或notifyAll()方法後,當前執行緒才被喚醒進入就緒狀態。

  notify():喚醒在此物件監控器(鎖物件)上等待的單個執行緒。

  notifyAll():喚醒在此物件監控器(鎖物件)上等待的所有執行緒。

注意:wait()、notify()、notifyAll()都依賴於同步鎖,而同步鎖是物件持有的,且每個物件只有一個,所以這些方法定義在Object類中,而不是Thread類中。

8.3、yield()、sleep()、wait()比較

   wait():讓執行緒從執行狀態進入等待阻塞狀態,並且會釋放它所持有的同步鎖。

   yield():讓執行緒從執行狀態進入就緒狀態,不會釋放它鎖持有的同步鎖。

   sleep():讓執行緒從執行狀態進入阻塞狀態,不會釋放它鎖持有的同步鎖。

9、執行緒的狀態


以上只是簡單的一個執行緒狀態圖,其實執行緒狀態博大精深,要講清楚還是要一大篇文筆,作為入門文章先了解一下吧,之後的併發程式設計文章將再講述吧!

如果想要去深入瞭解一下的話也是可以的:Java執行緒的6種狀態及切換

10、執行緒池

在java中只要說到池,基本都是一個套路,啥資料庫連線池、jdbc連線池等,思想基本上就是:一個容納多個要使用資源的容器,其中的資源可以反覆使用,省去了頻繁建立執行緒物件的操作,無需反覆建立資源而消耗過多資源。

10.1、執行緒池概述

執行緒池其實就是一個容納多個執行緒的容器,其中的執行緒可以反覆使用,省去了頻繁建立執行緒物件的操作,無需反覆建立執行緒而消耗過多資源。

合理利用執行緒池能夠帶來三個好處:

  1. 降低資源消耗。減少了建立和銷燬執行緒的次數,每個工作執行緒都可以被重複利用,可執行多個任務。
  2. 提高響應速度。當任務到達時,任務可以不需要的等到執行緒建立就能立即執行。
  3. 提高執行緒的可管理性。可以根據系統的承受能力,調整執行緒池中工作線執行緒的數目,防止因為消耗過多的記憶體,而把伺服器累趴下(每個執行緒需要大約1MB記憶體,執行緒開的越多,消耗的記憶體也就越大,最後宕機)。

10.2、 執行緒池的使用

Java裡面執行緒池的最頂級介面是java.util.concurrent.Executor,但是嚴格意義上講Executor並不是一個執行緒池,而只是一個執行執行緒的工具。真正的執行緒池介面是java.util.concurrent.ExecutorService

要配置一個執行緒池是比較複雜的,尤其是對於執行緒池的原理不是很清楚的情況下,很有可能配置的執行緒池不是較優的,因此在java.util.concurrent.Executors執行緒工廠類裡面提供了一些靜態工廠,生成一些常用的執行緒池。官方建議使用Executors工程類來建立執行緒池物件。

Executors類中有個建立執行緒池的方法如下:

  • public static ExecutorService newFixedThreadPool(int nThreads):返回執行緒池物件。(建立的是有界執行緒池,也就是池中的執行緒個數可以指定最大數量)

獲取到了一個執行緒池ExecutorService 物件,那麼怎麼使用呢,在這裡定義了一個使用執行緒池物件的方法如下:

  • public Future<?> submit(Runnable task):獲取執行緒池中的某一個執行緒物件,並執行

Future介面:用來記錄執行緒任務執行完畢後產生的結果。執行緒池建立與使用。

使用執行緒池中執行緒物件的步驟:

  1. 建立執行緒池物件。
  2. 建立Runnable介面子類物件。(task)
  3. 提交Runnable介面子類物件。(take task)
  4. 關閉執行緒池(一般不操作這一步)。

Runnable實現類程式碼:

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("我要一個游泳教練");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("教練來了: " + Thread.currentThread().getName());
        System.out.println("教我游泳,教會後,教練又回到了游泳池");
    }
}

執行緒池測試類:

public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 建立執行緒池物件
        ExecutorService service = Executors.newFixedThreadPool(2);//包含2個執行緒物件
        // 建立Runnable例項物件
        MyRunnable r = new MyRunnable();

        //自己建立執行緒物件的方式
        // Thread t = new Thread(r);
        // t.start(); ---> 呼叫MyRunnable中的run()

        // 從執行緒池中獲取執行緒物件,然後呼叫MyRunnable中的run()
        service.submit(r);
        // 再獲取個執行緒物件,呼叫MyRunnable中的run()
        service.submit(r);
        service.submit(r);
        // 注意:submit方法呼叫結束後,程式並不終止,是因為執行緒池控制了執行緒的關閉。
        // 將使用完的執行緒又歸還到了執行緒池中
        // 關閉執行緒池
        //service.shutdown();
    }
}

以上只是簡單的使用執行緒池,僅僅是入門階段!道阻且長,路還很長....

到這裡,本文章入門暫時告一段落,以後有時間儘量抽空更新....

如果本文章對你有幫助,哪怕是一點點,請點個讚唄,謝謝你~

歡迎各位關注我的公眾號,一起探討技術,嚮往技術,追求技術...說好了來了就是盆友喔...

相關推薦

文章絕對深刻理解java類的載入以及ClassLoader原始碼分析【JVM二】

目錄 1、什麼是類的載入(類初始化) 2、類的生命週期 3、介面的載入過程 4、解開開篇的面試題 5、理解首次主動使用 6、類載入器 7、關於名稱空間

文章就是要入門java執行

目錄 1. 理解執行緒與程序 2、理解並行和併發 3、特殊的一個單執行緒:主執行緒(Main執行緒) 4、 建立多執行緒的四種方式 5、使用匿名內部類方式建立執行緒 6、執行緒

Android介面回撥機制(有圖有真相如果一直接觸介面回撥但是自己又一直理解不文章秒懂)

本人自己也經常在開發中遇到介面回撥機制,說來慚愧,雖然一直在用介面,但是隻是去複製別人的程式碼,一直弄不明白介面回撥的原理,甚至自己也努力去看了很多篇文章,幾乎看了所有的介面回撥的例子,什麼延時問答啊,孔融讓梨啊,都看了,例子都能背下來了,兩三個月了還沒理解。看來本人頭腦不

JAVA執行————一文章徹底征服執行開發(一)

多執行緒的基本概念 執行緒指程序中的一個執行場景,也就是執行流程,那麼程序和執行緒有什麼區別呢? 每個程序是一個應用程式,都有獨立的記憶體空間 同一個程序中的執行緒共享其程序中的記憶體和資源(共享的記憶體是堆記憶體和方法區記憶體,棧記憶體不共享,

文章徹底征服執行開發

多執行緒的基本概念 執行緒指程序中的一個執行場景,也就是執行流程,那麼程序和執行緒有什麼區別呢? 每個程序是一個應用程式,都有獨立的記憶體空間 同一個程序中的執行緒共享其程序中的記憶體和資源(共享的記憶體是堆記憶體和方法區記憶體,棧記憶體不共享,

JAVA執行————一文章徹底征服執行開發

多執行緒的基本概念執行緒指程序中的一個執行場景,也就是執行流程,那麼程序和執行緒有什麼區別呢?每個程序是一個應用程式,都有獨立的記憶體空間同一個程序中的執行緒共享其程序中的記憶體和資源(共享的記憶體是堆記憶體和方法區記憶體,棧記憶體不共享,每個執行緒有自己的。)什麼是程序?一

java工程師linux命令文章就夠

sep 實戰 reads href 改變 sig 腳本 喜歡 這樣的 精彩內容推薦 一套java架構師學習資源,等你拿 你所需要的大數據視頻教程 微服務資源springboot、springcloud、docker、dubbo項目實戰等傾心分享 shell簡介

關於深度學習中的注意力機制文章從例項到原理都幫參透(很系統重點看)

最近兩年,注意力模型(Attention Model)被廣泛使用在自然語言處理、影象識別及語音識別等各種不同型別的深度學習任務中,是深度學習技術中最值得關注與深入瞭解的核心技術之一。 本文以機器翻譯為例,深入淺出地介紹了深度學習中注意力機制的原理及關鍵計算機制,同時也抽

時間管理文章就夠

hhhh,好像有點標題黨的味道。今天分享一下時間管理的技能,時間管理不論是在讀書的時候,還是在工作的時候,都特別重要。我一直覺得Life=Time. 之前接觸過時間管理,走了很多彎路,自己也讀過什麼《高效能人士的七個臭毛病》這樣的書,後來實際上發現沒啥用,自己

【萬字長文】再報班文章入門Python

本文始發於個人公眾號:**TechFlow**,原創不易,求個關注 最近有許多小夥伴後臺聯絡我,說目前想要學習Python,但是沒有一份很好的資料入門。一方面的確現在市面上Python的資料過多,導致新手會不知如何選擇,另一個問題很多資料內容也很雜,從1+1到深度學習都包括,純粹關注Python本身語法

如何快速入門python文章指明方向(零基礎的福音)

一個 交流群 企業 小項目 調用 不錯 數據類型 數據 入門 這是曾經在悟空問答回答的一個問題,後來效果還不錯,所以發出來,裏面結合了當年的學習經驗和一些行業老師的建議,希望幫助更多有興趣的人。(第三點福利) Python語言這幾年大火,在世界編程語言排行中Python也位

爬蟲技術的門道文章總結的最全

Web是一個開放的平臺,這也奠定了Web從90年代初誕生直至今日將近30年來蓬勃的發展。然而,正所謂成也蕭何敗也蕭何,開放的特型、搜尋引擎以及簡單易學的HTML、CSS技術使得Web成為了網際網路領域裡最為流行和成熟的資訊傳播媒介;但如今作為商業化軟體,Web這個平臺上的內容資訊的版權卻毫無保證,因為

Android Thread之threadLoop方法——(追IMS原始碼時不知怎麼執行到threadLoop方法的文章有一個很好的解釋)

Android Framework中的執行緒Thread及它的threadLoop方法 在Framework中的Thread普遍的特點就是有一個 threadLoop方法。它到底是怎麼迴圈起來的。 Android中java世界的Thread 先來看看java是怎麼建

解決:百度編輯器UEditor怎麼將圖片儲存到圖片伺服器或者上傳到ftp伺服器的問題(如果正在用UE文章值得看下)

在使用百度編輯器ueditor的時候,怎麼將圖片儲存到另一個伺服器,或者上傳到ftp伺服器?這個問題,估計很多使用UE的人會遇到。而且我百度過,沒有找到這個問題的解決方案。那麼:本篇文章就很適合你了。因為本文解決了這個問題。 1.當你使用UEditor(以下簡稱UE)的

Java執行文章足矣

引如果對什麼是執行緒、什麼是程序仍存有疑惑,請先Google之,因為這兩個概念不在本文的範圍之內。用多執行緒只有一個目的,那就是更好的利用cpu的資源,因為所有的多執行緒程式碼都可以用單執行緒來實現。說這個話其實只有一半對,因為反應“多角色”的程式程式碼,最起碼每個角色要給他一個執行緒吧,否則連實際場景都無法

如果再有人問分散式ID文章丟給他

首先國慶節要到了,先提前祝大家節日快樂,當然在放假的時候適當的學一下知識也是必要的。1.背景在我

【東北哭文章不是原創,感謝寫文章的人

這篇文章不是原創,感謝寫這篇文章的人   ­    東北人火爆。有人說在“民風剽悍”的東北,生男孩兒是件很麻煩的事。幾乎沒有哪個東北男人不是打架長大的。是啊,東北人就是這性格,有了什麼不痛快,沒心思跟你廢話,拳頭飛腳先行,成敗論英雄。打贏的孩子撲撲身上的土,回家了,別家的家長找來,老爹要訓斥的,“小犢子!就不

如果再有人問分散式 ID文章丟給他

開發十年,就只剩下這套架構體系了! >>>   

Android9.0來6個重大更新得知道

原標題:Android9.0來了,這6個重大更新你得知道 三月份是手機密集釋出的日子,從MWC之後, 三月份是手機密集釋出的日子,從MWC之後,國內外不少廠商都推出了自家的新品。如果說去年的浪潮是全面屏設計的話,今年的趨勢恐怕就是劉海屏了。不管喜不喜歡,我們都要承認劉海屏是提高屏佔比

#一文章瞭解四種執行學習Java不在困惑

在Java開發中,有時遇到多執行緒的開發時,直接使用Thread操作,對程式的效能和維護上都是一個問題,使用Java提供的執行緒池來操作可以很好的解決問題,於是找了下API看到Java提供四種執行緒池使用,Java通過Executors提供四種執行緒池,分別為: 1、newCachedThrea