java併發編程之 java線程基本概念

閱讀建議

  1. 最好使用電腦觀看。
  2. 若是你非要使用手機觀看,那請把字體調整到最小,這樣觀看效果會好一些。
  3. 碎片化閱讀並不會獲得真正的知識提高,要想有提高還得找張書桌認認真真看一會書,或者咱們公衆號的文章。
  4. 若是以爲不錯,各位幫着轉發轉發,若是以爲有問題或者寫的哪不清晰,務必私聊我~
  5. 本篇文章不是java語法的基本教程!在閱讀以前,請保證你有面向對象的編程基礎,熟悉封裝繼承多態,不然的話,你不適合閱讀本篇文章,先學一下基礎吧~

Java線程基本概念

哈哈,上邊扯了好大一下子犢子才繞到java語法這😅。沒辦法呀,你不瞭解故事背景直接看劇情老是會有些懵逼的~java語言中的線程是對操做系統線程的一種抽象,有些地方可能不太一致,遇到了再和你們說哈~java

main線程

咱們以前說過,main方法是程序入口,咱們對已經編譯好的class文件調用java命令時就能夠運行一個java程序。這個過程當中,其實系統自動爲咱們建立了一個進程和一個線程,並且這個線程的名字就叫作mainmain線程是用來執行咱們的程序的,不過系統還會爲咱們建立一些輔助線程來幫助main線程的執行,如今就先不說它們是啥了,等遇到了再說哈~編程

除了系統本身建立的這個main線程之外,咱們還能夠本身在程序裏建立一些線程。不過咱們前邊說過,線程實際上是去執行任務的,因此咱們先看怎麼定義任務ide

定義任務

java中的任務被抽象成了一個Runnable接口字體

public interface Runnable {
    public void run();
}

咱們的自定義任務須要去實現這個接口,並把任務的詳細內容寫在覆蓋的run方法裏,好比咱們定義一個輸出一個字符串的任務:操作系統

public class PrintTask implements Runnable {

    @Override
    public void run() {
        System.out.println("輸出一行字");
    }
}

看到了吧,定義一個任務就是這麼簡單哈~不過光有任務沒啥卵用,須要建立一個線程去運行這個任務線程

Thread類

java中的Thread類來表明一個線程,咱們須要關注它的這幾種構造方法:設計

  • Thread(Runnable target, String name)

    在建立線程對象的時候傳入須要執行的任務以及這個線程的名稱。code


  • Thread(Runnable target)
    只傳入須要執行的任務,名稱是系統自動生成的,或者能夠在建立對象後再經過別的方法修更名稱。

  • Thread(String name)
    只傳入待建立線程的名稱。

  • Thread()
    啥都不傳,就是單純構造一個線程對象而已~

執行任務對象

Thread類的start()方法負責開始執行一個線程,讓一個線程運行起來有這麼兩種方法:繼承

  1. 建立Thread對象的時候指定須要執行的任務
public class Test {

    public static void main(String[] args) {
        new Thread(new PrintTask()).start();
    }
}

執行結果是:

輸出一行字
  1. 經過繼承Thread類並覆蓋run方法:

Thread類自己就表明了一個Runnable任務,咱們看Thread類的定義:

public class Thread implements Runnable {

    private Runnable target;

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

    // ... 爲省略篇幅,省略其餘方法和字段
}

其中的target就是在構造方法裏傳入的,若是構造方法不傳這個字段的話,很顯然run方法就是一個空實現,因此若是咱們想運行這個線程,就繼承它而且覆蓋一下run方法吧:

public class PrintThread extends Thread {

    @Override
    public void run() {
        System.out.println("輸出一行字");
    }
}

由於PrintThread中已經有一個任務了,因此直接調用start方法運行它就好:

public class Test {

    public static void main(String[] args) {
        new PrintThread().start();
    }
}

執行結果是:

輸出一行字

這兩種執行任務的方法說不上誰好誰壞,可是使用繼承Thread類而且覆蓋run方法的方式把線程和任務給弄到了一起了,不可分割了,也就是所謂的耦合了,因此咱們平時更傾向於使用任務和線程分開處理的第1種執行任務的方式。固然,有時候爲了演示的方便,也是會使用繼承Thread類而且覆蓋run方法的方式~

線程相關方法

Thread類提供了許多方法來方便咱們獲取線程的信息或者控制線程,下邊來一下都有哪些重要的方法吧:

獲取線程ID

  • long getId():系統會爲每一個線程自動分配一個long類型的id值,經過getId方法能夠獲取這個值。
System.out.println(new Thread().getId());
System.out.println(new Thread().getId());
System.out.println(new Thread().getId());

執行結果:

10
11
12

獲取和設置線程名稱

  • void setName(String name):設置線程的名稱。
  • String getName():獲取線程的名稱。

固然,咱們也能夠經過構造方法去設置Thread的名稱:

Thread t1 = new Thread("t1");
Thread t2 = new Thread();
t2.setName("t2");
System.out.println("t1線程的名稱是:" + t1.getName());
System.out.println("t2線程的名稱是:" + t2.getName());

執行結果是:

t1線程的名稱是:t1
t2線程的名稱是:t2

設置線程的優先級
咱們知道處理器會從就緒的隊列裏挑一個已經就緒的線程去執行,每一個線程均可以有不一樣的優先級,優先級越高,越容易被處理器選中執行。

  • void setPriority(int newPriority):設置線程優先級。

java中的優先級是用一個正數來表示,共有1~10個等級,其中,設計java的大叔們用了是三個靜態變量表示咱們經常使用的:

  • Thread.MIN_PRIORITY = 1;
  • Thread.NORM_PRIORITY = 5;
  • Thread.MAX_PRIORITY = 10;

通常狀況下,咱們就用這三個變量去表示優先級就夠用了。

  • int getPriority():獲取線程的優先級

下面看個例子:

Thread t1 = new Thread("t1");
System.out.Println("t1線程的優先級是:" + t1.getpriority());
t1.setPriority(Thread.MAX_PRIORITY);
System.out.Println("t1線程的優先級是:" + t1.getpriority());

注意:線程優先級並不意味着得不處處理器執行,而只是執行的頻次低一點而已。並且線程的優先級通常不用咱們主動去設置,因此這兩個方法對咱們來講基本沒啥用~
休眠
若是想在線程執行過程當中讓程序停一段時間以後再執行,這個中止一段時間也叫作休眠,就好像睡一段時間而後醒來。能夠經過sleep()方法來實現休眠:

  • static void sleep(long millis) throws InterruptedException

程序在指定的毫秒數加納秒數內讓當前正在執行的線程休眠,也就是暫停執行。

  • static void sleep(long millis, int nanos) throws InterruptedException

程序在指定的毫秒數加納秒數內讓當前正在執行的線程休眠,也就是暫停執行。

你們注意到這個sleep方法是一個靜態方法,它會讓當前線程暫停指定的時間。這個所謂的暫停,或者說休眠其實只是把正在運行的線程阻塞掉,放到阻塞隊列裏,等指定的時間一到,再從阻塞的隊列裏出來而已。另外,這個方法有InterruptedException的異常,說明咱們在調用的時候須要catch一下:

public static void main(String[] args) {
    System.out.println(1);

    try {
        Thread.sleep(1000L);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }

    System.out.println(2);

    try {
        Thread.sleep(1000L);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }

    System.out.print(3);
}

你們在執行的過程當中會發現,每隔一秒會輸出一個數字。另外,因爲是在main方法中調用的sleep方法,因此其實休眠的是main線程,你們能夠試試在本身的線程裏休眠哈~

讓出本次處理器的時間片
咱們知道線程是處理器時間片的分配單位。不一樣的線程排着對等着處理器賞賜給一個時間片用來執行一下子代碼。若是一個線程已經得到了一個時間片正在執行,它忽然不想執行了,能夠放棄這次的時間片時間,先讓處理器給別的線程分配一個時間片,而他參與下一輪的時間分配。

舉個例子:好比說咱們把處理器比做皇帝,把各個線程比做妃子,各個妃子爭相讓皇帝寵幸,爲了公平,皇帝只能一次寵幸一個妃子五分鐘。皇帝怎麼挑選妃子有他本身的小算盤,可是,每一個五分鐘都會選一個去陪她。此時有一個妃子被皇帝挑選中了,但是她配了皇帝五分鐘有些尿急,因此她主動說先放棄此次寵幸,先出去放放水,而後再排隊被挑選吧。因此只陪了皇帝兩分鐘的她便退出了這次寵幸,放水回來後再加入到覅誒這大軍中等待皇帝挑選。

  • static void yield():表示放棄這次時間片時間,等待下次執行。

這個yield方法只是建議處理器不要在這次時間片時間內繼續執行本線程,最後實際怎麼着還不必定呢,另外,yield是一個靜態方法,表示讓出當前線程本次時間片的時間。

也就是說你想放棄就放棄是不可能的,這還得看皇帝的心情~
相關文章
相關標籤/搜索