Java多線程(三)關于多線程管理的相關函數(shù)說明

接著前面的多線程(二)的內容嘱蛋,下面我們接著來探討多個線程創(chuàng)建之后蚯姆,關于線程調度和管理的一些方法。

先來簡單介紹下線程調度###

對于計算機的CPU(以單核為例)洒敏,在任意時刻只能執(zhí)行一條指令龄恋,每個線程只有獲得CPU的使用權才能執(zhí)行指令。當有多個處于可運行狀態(tài)的線程在等待CPU凶伙,JVM的一項任務就是負責線程的調度郭毕。JVM按照特定機制為多個線程分配CPU的使用權過程就是線程調度。

線程調度的兩種模型:分時調度模型和搶占式調度模型函荣。
① 分時調度模型是指讓所有的線程輪流獲得cpu的使用權,并且平均分配每個線程占用的CPU的時間片显押,這個比較好理解。
② JVM采用搶占式調度模型傻挂,就是讓線程搶奪CPU資源乘碑,運行順序是不確定的,優(yōu)先權高的線程金拒,會有一定幾率優(yōu)先占用CPU兽肤。處于運行狀態(tài)的線程會一直運行下去,直至它不得不放棄CPU殖蚕。比如線程運行完畢轿衔、線程阻塞、運行被打斷睦疫。關于線程的多種運行狀態(tài)詳見:Java中的多線程(二)線程的創(chuàng)建及線程的生命周期。當線程被中斷時鞭呕,CPU會保存當前線程的狀態(tài)蛤育,以備此線程被喚醒時繼續(xù)執(zhí)行。

線程管理之設置線程優(yōu)先級###

Java線程優(yōu)先級共有10個級別,優(yōu)先級較高的線程會獲得較多的運行機會瓦糕,取值范圍是從1到10底洗。如果小于1或大于10,則JDK拋出異常IllegalArgumentException()咕娄。Thread類有以下三個靜態(tài)常量:
① static final int MAX_PRIORITY :值為10亥揖,代表最高優(yōu)先級。
② static final int MIN_PRIORITY :值為1圣勒,代表最低優(yōu)先級费变。
③ static final int NORM_PRIORITY:值為5,代表默認優(yōu)先級圣贸。

這里要注意的是:
① 并不是說優(yōu)先級較高的線程一定會在優(yōu)先級較低的線程之前運行挚歧,優(yōu)先級高這里是指獲得較多的運行機會。
② 優(yōu)先級高的線程會大部分先執(zhí)行完吁峻,并不一定會全部執(zhí)行完畢滑负。
③ 子線程的優(yōu)先級是跟父類優(yōu)先級是一樣的。

設置和獲取優(yōu)先級方法:
thread.setPriority(Thread.MIN_PRIORITY)和 thread.getPriority()

線程管理之守護線程###

我們在程序中創(chuàng)建的線程默認都是用戶線程(User Thread)用含。與用戶線程對應則是守護線程(Daemon Thread)矮慕,也可稱之為后臺線程,它的作用就是為其它線程提供服務的啄骇。守護線程使用的情況較少凡傅,舉例來說:JVM的垃圾(GC)回收線程就是守護線程。

需要注意的是:
① 當所有的用戶線程都結束退出的時候肠缔,守護線程也就沒啥可服務的了夏跷,隨著線程的結束而結束。如果JVM只剩下守護線程明未,虛擬機就會退出槽华。
② 守護線程會隨時中斷,因此不要在如輸入輸出流趟妥,數(shù)據(jù)庫連接等場合使用守護線程猫态。
③ 守護線程并非是JVM內部可提供,我們自己可以根據(jù)需要來設定守護線程披摄∏籽可以通過isDaemon和setDaemon方法來判斷和設置一個線程為守護線程。
④ 守護線程必須在start方法前設置疚膊,否則會拋出IllegalThreadStateException異常义辕。
⑤ 一個守護線程創(chuàng)建的子線程依然是守護線程。

package com.Dan;

public class Main {

    public static void main(String[] args) {
        // write your code here
        DaemonThread daemonThread = new DaemonThread();
        // 設置為守護線程
        daemonThread.setDaemon(true);
        daemonThread.start();
    }
}

class DaemonThread extends Thread {
    public void run() {
        System.out.println(Thread.currentThread().getName() + " start");

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + " end");
    }
}
// Thread-0 start
// 子線程需要執(zhí)行1秒寓盗,因為設置被設置為守護線程灌砖,因此主線程不會等待子線程執(zhí)行結束璧函,而是提前退出。
// 所有用戶線程都退出了基显,守護線程也就退出了蘸吓,因此并沒有打印end。

線程管理之常用方法###

一些簡單方法#####

public static int activeCount():返回當前線程的線程組中活動線程的數(shù)目撩幽。
public static Thread currentThread():返回對當前正在執(zhí)行的線程對象的引用库继。
public long getId():返回該線程的標識符,線程 ID 是唯一的窜醉。
public final void setName(String name):改變線程名稱宪萄。
public final String getName():返回該線程的名稱。
public String toString():返回該線程的字符串表示形式酱虎,包括線程名稱雨膨、優(yōu)先級和線程組。
public void start():使該線程開始執(zhí)行读串,Java 虛擬機調用該線程的 run 方法聊记。

下面是一些比較重要的方法:

sleep()方法 線程睡眠#####

sleep方法作用就是讓當前線程休眠,交出CPU恢暖,讓CPU去執(zhí)行其他的任務排监。當前線程是指this.currentThread()返回的線程。sleep方法使線程轉到阻塞狀態(tài)杰捂,當睡眠結束后舆床,就轉為可運行狀態(tài)。

package com.Dan;

/**
 * Created by daniel on 17/3/27.
 */
public class 多線程博客三 {
    public static void main(String[] args) {

        System.out.println(Thread.currentThread().getName()+"主線程開始執(zhí)行嫁佳。");
        TaskThread thread1 = new TaskThread("線程-A-");
        TaskThread thread2 = new TaskThread("線程-B-");
        thread1.start();
        thread2.start();
        System.out.println(Thread.currentThread().getName()+ "主線程運行結束挨队。");
    }
}

class TaskThread extends Thread{
    private String name;

    public TaskThread(String name) {
        this.name = name;
    }
    @Override
    public void run() {

        System.out.println(Thread.currentThread().getName() + "子線程運行開始。");
        for (int i = 0; i < 5; i++) {
            System.out.println(name + "運行: " + i);
            try {
                sleep((long) Math.random() * 100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + "子線程運行結束!");
    }
}

// main主線程開始執(zhí)行蒿往。
// Thread-0子線程運行開始盛垦。
// Thread-1子線程運行開始。
// 線程-B-運行: 0
// main主線程運行結束瓤漏。
// 線程-A-運行: 0
// 線程-B-運行: 1
// 線程-A-運行: 1
// 線程-B-運行: 2
// 線程-A-運行: 2
// 線程-B-運行: 3
// 線程-A-運行: 3
// 線程-B-運行: 4
// 線程-A-運行: 4
// Thread-1子線程運行結束腾夯。
// Thread-0子線程運行結束。

// sleep方法使當前線程休眠蔬充,交出CPU蝶俱,讓CPU去執(zhí)行其他的任務。這里的主線程饥漫,早在子線程開始后就馬上結束了榨呆。
join()線程加入#####

在當前線程中調用另一個線程的join方法,則當前線程轉入阻塞狀態(tài)趾浅,直到另一個進程運行結束愕提,當前線程再由阻塞轉為可運行狀態(tài)馒稍。如果調用此方法時皿哨,另一個線程已經(jīng)運行完畢浅侨,那就接著運行當前線程。

package com.Dan;

/**
 * Created by daniel on 17/3/27.
 */
public class ThreadJoin {
    
    public static void main(String[] args) {
        
        Thread thread = new Thread(new Runnable() { //匿名對象
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + " 子線程 " + i);
                }
            }
        });
        
        thread.start();
        
        for (int i = 0; i < 10; i++) {
            if (i == 5) {
                try {
                    thread.join(); // 此時調用Thread線程
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + " 主線程 " + i);
        }
    }
}

// main 主線程 0
// main 主線程 1
// main 主線程 2
// main 主線程 3
// main 主線程 4
// Thread-0 子線程 0
// Thread-0 子線程 1
// Thread-0 子線程 2
// Thread-0 子線程 3
// Thread-0 子線程 4
// Thread-0 子線程 5
// Thread-0 子線程 6
// Thread-0 子線程 7
// Thread-0 子線程 8
// Thread-0 子線程 9
// main 主線程 5
// main 主線程 6
// main 主線程 7
// main 主線程 8
// main 主線程 9
yield()線程讓步#####

暫停當前正在運行的線程证膨,把運行機會讓給其它的線程如输。這里的暫停,并不是讓線程轉到阻塞或等待狀態(tài)央勒,而是返回可運行狀態(tài)不见,等待被調度運行。需要注意的是崔步,此時讓步的線程是可運行狀態(tài)稳吮,它有可能會被再次運行。

package com.Dan;

/**
 * Created by daniel on 17/3/24.
 */

public class JavaEveryDay0324 extends Thread{

    public static void main(String[] args) {
        JavaEveryDay0324 test1 = new JavaEveryDay0324();
        JavaEveryDay0324 test2 = new JavaEveryDay0324();
        test1.start();
        test2.start();
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "  1");

        yield();
        System.out.println(Thread.currentThread().getName() + "  2");
    }
}

// Thread-0  1
// Thread-1  1
// Thread-0  2
// Thread-1  2
// 線程test1在輸出1后井濒,執(zhí)行yield()方法進入可運行狀態(tài)灶似,然后將CPU讓給線程test2。同樣線程test2運行輸出1后瑞你,執(zhí)行yield()方法也進入了可運行狀態(tài)酪惭,將CPU又讓給線程test1,線程test1繼續(xù)執(zhí)行者甲,打印輸出2春感,然后線程test2執(zhí)行輸出2。

// Thread-0  1
// Thread-0  2
// Thread-1  1
// Thread-1  2
// 也有可能出現(xiàn)這種情況虏缸,多運行幾次鲫懒,肯定會有的。

// Thread-0  1
// Thread-1  1
// Thread-1  2
// Thread-0  2
// 又或者是這一種刽辙。只要記住它返回的是可運行狀態(tài)窥岩,不是阻塞,也不是等待扫倡。那么不同的結果就能解釋清楚了谦秧。
** interrupt()線程中斷信號**#####

interrupt():這里只談中斷信號,中斷線程后續(xù)博客會更新撵溃。它向線程發(fā)送一個中斷信號疚鲤,強制結束調用該方法的線程當前狀態(tài),讓線程在無限等待時(如死鎖時)能拋出異常缘挑。

package com.Dan;

/**
 * Created by daniel on 17/3/27.
 */
public class ThreadInterrupt {

    public static void main(String[] args) {

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000000); // 別想醒過來了
                } catch (InterruptedException e) {
                    //e.printStackTrace();
                    System.out.println("拋異常嘍吆唉哈咦集歇。");
                }
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + " 子線程 " + i);
                }
            }
        });

        thread.start();

        for (int i = 0; i < 6; i++) {
            if (i == 2) {
                thread.interrupt(); // 終止當前線程的狀態(tài),并拋出個異常: e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "輸出:" + i);
        }
    }
}

// main輸出:0
// main輸出:1
// 拋異常嘍吆唉哈咦语淘。
// main輸出:2
// Thread-0 子線程 0
// Thread-0 子線程 1
// Thread-0 子線程 2
// Thread-0 子線程 3
// Thread-0 子線程 4
// Thread-0 子線程 5
// main輸出:3
// main輸出:4
// Thread-0 子線程 6
// Thread-0 子線程 7
// Thread-0 子線程 8
// Thread-0 子線程 9
// main輸出:5

** wait()線程等待**#####

這里先簡單介紹下wait方法诲宇,我們會在下一篇博客探討線程同步相關知識的時候际歼,再來探討它。

在線程通信中姑蓝,wait方法經(jīng)常與notify和notifyAll方法一起使用鹅心。,他們并不是Thread類的方法纺荧,而是Object類的方法旭愧。
① wait:當達到某種狀態(tài)的時候,wait方法讓線程進入等待狀態(tài)宙暇,讓本線程休眠输枯。線程自動釋放其占有的對象鎖,并等待notify占贫。直到有其它線程調用對象的notify方法喚醒該線程桃熄,才能繼續(xù)獲取對象鎖,繼續(xù)運行型奥。
② notify:喚醒一個正在等待當前對象鎖的線程瞳收,并讓它拿到對象鎖。
③ notifyAll:喚醒所有正在等待前對象鎖的線程桩引。

需要注意的是:
① 在調用這3個方法的時候缎讼,當前線程必須獲得這個對象的鎖,也就是說這三個方法必須在synchronized(Obj){...}內部坑匠。
② 在調用notify方法后血崭,并不會馬上釋放對象鎖,而是在synchronized(){}執(zhí)行結束的時候厘灼,自動釋放鎖夹纫,JVM會隨機喚醒一個正在等待當前對象鎖的線程,讓他獲得對象鎖设凹,喚醒線程刊苍。

** wait()與sleep()方法的區(qū)別**#####

① sleep方法是一個靜態(tài)方法搜立,作用在當前線程上桩匪。而wait方法是一個實例方法捐川,并且只能在其他線程調用本實例的notify方法時被喚醒。
② wait只能在線程同步環(huán)境中被調用奋姿,會釋放鎖锄开。而sleep不限制使用環(huán)境,當在一個Synchronized塊中調用Sleep方法時称诗,線程雖然休眠了萍悴,但是對象的鎖并木有被釋放,其他線程無法訪問這個對象。
③ sleep必須捕獲異常癣诱,而wait计维,notify和notifyAll則不需要。
④ 進入等待狀態(tài)的線程能夠被notify方法喚醒撕予。sleep休眠時間到了鲫惶,該線程不一定會立即執(zhí)行,因為其它線程可能正在運行嗅蔬。
⑤ 如果你需要暫停某個線程一段特定的時間剑按,就使用sleep方法疾就。如果你想要實現(xiàn)線程間通信就使用wait方法澜术。

** 關于 “調用yield()方法后,會選擇同等優(yōu)先級的線程繼續(xù)執(zhí)行猬腰∧穹希” 勘誤**#####

看到網(wǎng)上有人說調用yield()方法后,會選擇同等優(yōu)先級或更高優(yōu)先級的線程繼續(xù)執(zhí)行姑荷。這個觀點是錯誤的盒延。具體執(zhí)行執(zhí)行哪個線程是由JVM說了算。請看下面的例子鼠冕,低優(yōu)先級的也會被執(zhí)行添寺。

// 在上面方法基礎上重新修改的
package com.Dan;

/**
 * Created by daniel on 17/3/24.
 */

public class JavaEveryDay0324 extends Thread{

    public static void main(String[] args) {
        JavaEveryDay0324 test3 = new JavaEveryDay0324();
        JavaEveryDay0324 test1 = new JavaEveryDay0324();
        JavaEveryDay0324 test2 = new JavaEveryDay0324();

        test3.setPriority(1);
        test2.setPriority(10);
        test1.setPriority(5);
        test3.start();
        test1.start();
        test2.start();
        System.out.println("線程1ID: "+test1.getId()+" 線程2ID: "+test2.getId()+" 線程3ID: "+test3.getId());

    }
    @Override
    public void run() {


        for (int i = 0; i < 3; i++) {
            System.out.println("線程ID為"+Thread.currentThread().getId()+": " + " 的第①次打印");

            yield();
            System.out.println("線程ID為"+Thread.currentThread().getId() +": " +  " 的第②次打印");
        }
    }
}

// 線程ID為10:  的第①次打印
// 線程ID為11:  的第①次打印
// 線程ID為12:  的第①次打印
// 線程1ID: 11 線程2ID: 12 線程3ID: 10
// 線程ID為10:  的第②次打印
// 線程ID為10:  的第①次打印
// 線程ID為11:  的第②次打印
// 線程ID為11:  的第①次打印
// 線程ID為12:  的第②次打印
// 線程ID為12:  的第①次打印
// 線程ID為10:  的第②次打印
// 線程ID為10:  的第①次打印
// 線程ID為11:  的第②次打印
// 線程ID為11:  的第①次打印
// 線程ID為12:  的第②次打印
// 線程ID為12:  的第①次打印
// 線程ID為10:  的第②次打印
// 線程ID為12:  的第②次打印
// 線程ID為11:  的第②次打印

寫完嘍!ㄟ(▔,▔)ㄏㄟ(▔,▔)ㄏㄟ(▔,▔)ㄏ


知識重在總結和梳理懈费,只有不斷地去學習并運用计露,才能化為自己的東西。當你能為別人講明白的時候憎乙,說明自己已經(jīng)掌握了票罐。

歡迎轉載,轉載請注明出處泞边!

如果有錯誤的地方该押,或者有您的見解,還請不嗇賜教阵谚!

喜歡的話蚕礼,麻煩點個贊!

Java中的多線程(一)多線程基礎之進程梢什、線程奠蹬、并發(fā)、并行绳矩。
Java中的多線程(二)線程的創(chuàng)建及線程的生命周期

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末罩润,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子翼馆,更是在濱河造成了極大的恐慌割以,老刑警劉巖金度,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異严沥,居然都是意外死亡猜极,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門消玄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來跟伏,“玉大人,你說我怎么就攤上這事翩瓜∈馨猓” “怎么了?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵兔跌,是天一觀的道長勘高。 經(jīng)常有香客問我,道長坟桅,這世上最難降的妖魔是什么华望? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮仅乓,結果婚禮上赖舟,老公的妹妹穿的比我還像新娘。我一直安慰自己夸楣,他們只是感情好宾抓,可當我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著裕偿,像睡著了一般洞慎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上嘿棘,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天劲腿,我揣著相機與錄音,去河邊找鬼鸟妙。 笑死焦人,一個胖子當著我的面吹牛,可吹牛的內容都是我干的重父。 我是一名探鬼主播花椭,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼房午!你這毒婦竟也來了矿辽?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎袋倔,沒想到半個月后雕蔽,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡宾娜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年批狐,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片前塔。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡嚣艇,死狀恐怖,靈堂內的尸體忽然破棺而出华弓,到底是詐尸還是另有隱情食零,我是刑警寧澤,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布该抒,位于F島的核電站慌洪,受9級特大地震影響,放射性物質發(fā)生泄漏凑保。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一涌攻、第九天 我趴在偏房一處隱蔽的房頂上張望欧引。 院中可真熱鬧,春花似錦恳谎、人聲如沸芝此。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽婚苹。三九已至,卻和暖如春鸵膏,著一層夾襖步出監(jiān)牢的瞬間膊升,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工谭企, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留廓译,地道東北人。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓债查,卻偏偏與公主長得像非区,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子盹廷,可洞房花燭夜當晚...
    茶點故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內容

  • 本文主要講了java中多線程的使用方法征绸、線程同步、線程數(shù)據(jù)傳遞、線程狀態(tài)及相應的一些線程函數(shù)用法管怠、概述等剥汤。 首先講...
    李欣陽閱讀 2,458評論 1 15
  • Java多線程學習 [-] 一擴展javalangThread類 二實現(xiàn)javalangRunnable接口 三T...
    影馳閱讀 2,965評論 1 18
  • 該文章轉自:http://blog.csdn.net/evankaka/article/details/44153...
    加來依藍閱讀 7,357評論 3 87
  • 寫在前面的話: 這篇博客是我從這里“轉載”的,為什么轉載兩個字加“”呢排惨?因為這絕不是簡單的復制粘貼吭敢,我花了五六個小...
    SmartSean閱讀 4,742評論 12 45
  • 早上去看了一場電影。 這兩天心情一直不好暮芭,周末了也一個人在賓館鹿驼,怕憋出病來,于是早上起來定了9:55的《木乃伊》辕宏。...
    Evan0827閱讀 238評論 0 0