Java多線程簡介

多線程在Java中無處不在粮揉,在上一篇(Java線程概念理解)中我們看到就算是一個最簡單的Java類中也涉及到了多線程,大家可能會疑惑吟策,為什么一個這么簡單的Java類中貌夕,卻啟動了那么多“無關”的線程,Java是不是將簡單的問題搞復雜了呢虏等?答案當然是否定了弄唧,這是因為正確的使用多線程能夠?qū)⒑臅r的處理大大的縮減時間,能夠讓用戶的體驗更加友好霍衫。使用多線程的主要原因有以下幾點:

  1. 更多的處理器核心

現(xiàn)代處理器的核數(shù)越來越多候引,以及超線程越來越廣泛的使用,現(xiàn)代計算機越來越擅長并行計算敦跌。我們知道一個程序作為一個進程在運行澄干,而一個進程可以創(chuàng)建多個線程,而一個線程在一個時刻只能運行在一個處理器核心上柠傍,試想一下一個單線程程序在運行時只能使用一個處理器核心麸俘,那么再多的處理器核心也無法提升該程序的執(zhí)行效率。

  1. 更快的響應時間

多線程能夠給用戶更快的響應時間惧笛,例如我們在做一些復雜的業(yè)務邏輯的時候从媚,我們先可以給用戶一個反饋,然后再將一些數(shù)據(jù)一致性不強的業(yè)務邏輯派發(fā)給其它線程去處理患整,這樣一來就大大縮短了響應時間拜效,提升了用戶體驗喷众。

  1. 更好的編程模型

Java為多線程提供了良好的編程模型,使開發(fā)者能夠更加專注于問題的解決紧憾,即為所遇到的問題建立合適的模型到千,而不是絞盡腦汁的如何將其多線程化。

1 線程優(yōu)先級

線程優(yōu)先級從低到高依次為1~10赴穗,在創(chuàng)建線程是可以使用setPriority(int newPriority)方法來設置線程等的優(yōu)先級憔四,默優(yōu)先級位是5。線程的優(yōu)先級控制的是線程獲取時間片段的多少般眉,也就是說優(yōu)先級高的線程分配時間片的數(shù)量多于優(yōu)先級低的線程了赵。但是這也不是絕對的,因為不同的JVM和操作系統(tǒng)上煤篙,線程的規(guī)劃會存在這差異斟览,有些操作系統(tǒng)甚至會忽略線程優(yōu)先級的設置毁腿,因此不要將線程的優(yōu)先級作為線程的運行順序以及程序的正確性的依賴辑奈。

線程優(yōu)先級設置

線程優(yōu)先級在Thread類中的定義如下:

/**
 * 線程可以擁有的最小優(yōu)先級
 */
public final static int MIN_PRIORITY = 1;

/**
 * 分配個線程的默認優(yōu)先級
 */
public final static int NORM_PRIORITY = 5;

/**
 * 線程可以擁有的最大優(yōu)先級
 */
public final static int MAX_PRIORITY = 10;


public final void setPriority(int newPriority) {
    //線程組
    ThreadGroup g;
    checkAccess();
    //優(yōu)先級范圍檢查
    if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
        throw new IllegalArgumentException();
    }
    if ((g = getThreadGroup()) != null) {
        if (newPriority > g.getMaxPriority()) {
            newPriority = g.getMaxPriority();
        }
        //設置線程優(yōu)先級
        setPriority0(priority = newPriority);
    }
}

// 最終調(diào)用本地方法設置優(yōu)先級
private native void setPriority0(int newPriority);

接下來我們看下線程優(yōu)先級的特性:

  • 線程優(yōu)先級的繼承性:如果一個線程A啟動了線程B,那么線程B的優(yōu)先級將會和線程A 的一致(線程B不顯示的設置線程優(yōu)先級)已烤。
  • 線程優(yōu)先級的隨機性:優(yōu)先級高的線程并不一定會先于優(yōu)先級低的線程執(zhí)行鸠窗。

下面我們來驗證下線程優(yōu)先級的這些特性:

public class ThreadPriority {
    public static void main(String[] args) {
        Thread1 t1 = new Thread1();
        t1.start();
        System.out.println("main的線程優(yōu)先級:" + Thread.currentThread().getPriority());
    }
}

class Thread1 extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "優(yōu)先級:" + Thread.currentThread().getPriority());
    }
}
output:
main的線程優(yōu)先級:5
Thread-0優(yōu)先級:5
public class ThreadPriority {
    public static void main(String[] args) {

        for (int i = 1; i <= 10; i++) {

            PriorityThread t = new PriorityThread("線程優(yōu)先級" + i);
            t.setPriority(i);
            t.start();

        }
    }
}

class PriorityThread extends Thread {
    public PriorityThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "開始執(zhí)行....");
        long beginTime = System.currentTimeMillis();
        long addResult = 0;
        for (int j = 0; j < 10; j++) {
            for (int i = 0; i < 50000; i++) {
                Random random = new Random();
                random.nextInt();
                addResult = addResult + i;
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("☆☆☆☆☆" + Thread.currentThread().getName() + " use time=" + (endTime - beginTime));
    }
}
output:
線程優(yōu)先級9開始執(zhí)行....
線程優(yōu)先級10開始執(zhí)行....
線程優(yōu)先級7開始執(zhí)行....
線程優(yōu)先級8開始執(zhí)行....
線程優(yōu)先級5開始執(zhí)行....
線程優(yōu)先級6開始執(zhí)行....
線程優(yōu)先級4開始執(zhí)行....
線程優(yōu)先級3開始執(zhí)行....
☆☆☆☆☆線程優(yōu)先級10 use time=157
線程優(yōu)先級2開始執(zhí)行....
☆☆☆☆☆線程優(yōu)先級9 use time=226
☆☆☆☆☆線程優(yōu)先級7 use time=273
☆☆☆☆☆線程優(yōu)先級8 use time=274
☆☆☆☆☆線程優(yōu)先級5 use time=354
☆☆☆☆☆線程優(yōu)先級6 use time=369
線程優(yōu)先級1開始執(zhí)行....
☆☆☆☆☆線程優(yōu)先級3 use time=572
☆☆☆☆☆線程優(yōu)先級4 use time=580
☆☆☆☆☆線程優(yōu)先級1 use time=223
☆☆☆☆☆線程優(yōu)先級2 use time=455

output:
線程優(yōu)先級7開始執(zhí)行....
線程優(yōu)先級8開始執(zhí)行....
線程優(yōu)先級9開始執(zhí)行....
線程優(yōu)先級10開始執(zhí)行....
線程優(yōu)先級5開始執(zhí)行....
☆☆☆☆☆線程優(yōu)先級9 use time=170
☆☆☆☆☆線程優(yōu)先級10 use time=185
線程優(yōu)先級6開始執(zhí)行....
☆☆☆☆☆線程優(yōu)先級7 use time=206
☆☆☆☆☆線程優(yōu)先級8 use time=228
線程優(yōu)先級1開始執(zhí)行....
☆☆☆☆☆線程優(yōu)先級5 use time=262
線程優(yōu)先級3開始執(zhí)行....
☆☆☆☆☆線程優(yōu)先級6 use time=84
線程優(yōu)先級2開始執(zhí)行....
線程優(yōu)先級4開始執(zhí)行....
☆☆☆☆☆線程優(yōu)先級4 use time=95
☆☆☆☆☆線程優(yōu)先級3 use time=112
☆☆☆☆☆線程優(yōu)先級2 use time=135
☆☆☆☆☆線程優(yōu)先級1 use time=230

從上面2個例子,我們就驗證了線程優(yōu)先級的繼承性和隨機性胯究。

2稍计、線程狀態(tài)

Java線程在運行的生命周期中可能處于以下幾種狀態(tài),在給定的時刻裕循,線程只能處于其中一種狀態(tài)臣嚣。線程狀態(tài)如下:

  1. NEW:初始狀態(tài),線程被構(gòu)建剥哑,但是還沒有調(diào)用start()方法硅则。
  2. RUNNABLE:運行狀態(tài),Java線程將操作系統(tǒng)中就緒和運行兩種狀態(tài)籠統(tǒng)的稱作“運行中”株婴。
  3. BLOCKED:阻塞狀態(tài)怎虫,表示線程阻塞于鎖。
  4. WAITING:等待狀態(tài)困介,表示線程進入等待狀態(tài)大审,進入該狀態(tài)表示當前線程需要等待其他線程做出一些特定動作(通知或中斷)
  5. TIME_WAITING:超時等待狀態(tài),該狀態(tài)不同于WAITING座哩,它是可以在指定的時間自行返回的徒扶。
  6. TERMINATED:終止狀態(tài),表示當前線程已經(jīng)執(zhí)行完畢根穷。

我們用一個示例來深入的理解現(xiàn)在的狀態(tài)酷愧,示例代碼如下:

public class ThreadState {
    public static void main(String[] args) {

        new Thread(new TimeWaiting(), "TimeWaitingThread").start();
        new Thread(new Waiting(), "WaitingThread").start();

        // 使用兩個Blocked線程驾诈,一個獲取鎖成功,另一個被阻塞
        new Thread(new Blocked(), "BlockedThread-1").start();
        new Thread(new Blocked(), "BlockedThread-2").start();
    }

    // 該線程不斷地進行睡眠
    static class TimeWaiting implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    SECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    // 該線程在Waiting.class實例上等待
    static class Waiting implements Runnable {
        @Override
        public void run() {
            while (true) {
                synchronized(Waiting.class) {
                    try {
                        Waiting.class.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    // 該線程在Blocked.class實例上加鎖后溶浴,不會釋放該鎖
    static class Blocked implements Runnable {
        public void run() {
            synchronized(Blocked.class) {
                while (true) {
                    try {
                        SECONDS.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

打開終端乍迄,輸入“jps”,輸出如下:
C:\Users\Administrator>jps
15680 Jps
16160 ThreadState
10772
15492 Launcher
3196 Launcher

可以看到運行示例對應的進程ID是16160,接著在輸入“jstack 16160”,部分輸入如下

C:\Users\Administrator>jstack 16160

// BlockedThread-2 線程阻塞在獲取Blocked.class示例的鎖上
"BlockedThread-2" #14 prio=5 os_prio=0 tid=0x0000000019399000 nid=0x1dec waiting for monitor entry [0x000000001a00f000]
  java.lang.Thread.State: BLOCKED (on object monitor)
        at com.zzw.juc.thread.ThreadState$Blocked.run(ThreadState.java:54)
        - waiting to lock <0x00000000d87c86a0> (a java.lang.Class for com.zzw.juc.thread.ThreadState$Blocked)
        at java.lang.Thread.run(Thread.java:748)

//  BlockedThread-1線程獲取到了Blocked.class的鎖
"BlockedThread-1" #13 prio=5 os_prio=0 tid=0x0000000019394000 nid=0x3e58 waiting on condition [0x0000000019f0f000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at java.lang.Thread.sleep(Thread.java:340)
        at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
        at com.zzw.juc.thread.ThreadState$Blocked.run(ThreadState.java:54)
        - locked <0x00000000d87c86a0> (a java.lang.Class for com.zzw.juc.thread.ThreadState$Blocked)
        at java.lang.Thread.run(Thread.java:748)

 // WaitingThread線程在Waiting實例上等待
"WaitingThread" #12 prio=5 os_prio=0 tid=0x00000000193a0000 nid=0x3ed0 in Object.wait() [0x0000000019e0f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000d87c5910> (a java.lang.Class for com.zzw.juc.thread.ThreadState$Waiting)
        at java.lang.Object.wait(Object.java:502)
        at com.zzw.juc.thread.ThreadState$Waiting.run(ThreadState.java:40)
        - locked <0x00000000d87c5910> (a java.lang.Class for com.zzw.juc.thread.ThreadState$Waiting)
        at java.lang.Thread.run(Thread.java:748)

 // TimeWaitingThread線程處于超時等待
"TimeWaitingThread" #11 prio=5 os_prio=0 tid=0x0000000019393000 nid=0x38d8 waiting on condition [0x0000000019d0e000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at java.lang.Thread.sleep(Thread.java:340)
        at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
        at com.zzw.juc.thread.ThreadState$TimeWaiting.run(ThreadState.java:27)
        at java.lang.Thread.run(Thread.java:748)

通過上面的示例士败,我們大概了解了Java程序運行中線程狀態(tài)的具體含義闯两。線程在自身的生命周期中,并不是固定地處于某個狀態(tài)谅将,而是隨著代碼的執(zhí)行在不同的狀態(tài)之間進行切換漾狼,Java線程狀態(tài)變遷如下圖所示:


線程1.png

從上圖我們可以看到,線程創(chuàng)建后調(diào)用start()方法開始執(zhí)行饥臂。當線程執(zhí)行了waite()方法之后逊躁,線程進入了等待狀態(tài),進入到等待狀態(tài)的線程需要其它線程的通知才能返回到運行狀態(tài)隅熙。超時等待狀態(tài)相當于在等待狀態(tài)上增加了超時時間的限制稽煤,也就是說當超時時間到達是線程會從超時等待狀態(tài)返回到運行狀態(tài)。當線程在競爭鎖失敗時囚戚,會進入阻塞狀態(tài)酵熙,當阻塞狀態(tài)的線程競爭鎖成功時,將進入運行狀態(tài)驰坊。線程在執(zhí)行完run()方法之后進入終止狀態(tài)匾二。
注意: 這里說的競爭鎖失敗,是線程在進入synchronized關鍵字修飾的方法或者代碼拳芙。而不是java并發(fā)包下的Lock接口實現(xiàn)的鎖察藐。在Lock鎖競爭失敗,線程進入的是等待狀態(tài)舟扎,因為java并發(fā)包中Lock接口對于阻塞的實現(xiàn)均使用了LockSupport類中的相關方法分飞。

3 Deamon線程

Deamon線程是一種支持型線程,它主要被用作程序中后臺調(diào)度已經(jīng)支持性工作浆竭。Deamon線程在主線程結(jié)束后將被直接關閉浸须。可以通過Thread.setDaemon(true)方法將線程轉(zhuǎn)換為Deamon線程邦泄。設置Deamon屬性必須在線程啟動之前設置删窒,線程啟動后設置的Deamon屬性將不起作用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末顺囊,一起剝皮案震驚了整個濱河市肌索,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌特碳,老刑警劉巖诚亚,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晕换,死亡現(xiàn)場離奇詭異,居然都是意外死亡站宗,警方通過查閱死者的電腦和手機闸准,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梢灭,“玉大人夷家,你說我怎么就攤上這事∶羰停” “怎么了库快?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長钥顽。 經(jīng)常有香客問我义屏,道長,這世上最難降的妖魔是什么蜂大? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任闽铐,我火速辦了婚禮,結(jié)果婚禮上县爬,老公的妹妹穿的比我還像新娘阳啥。我一直安慰自己添谊,他們只是感情好财喳,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著斩狱,像睡著了一般耳高。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上所踊,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天泌枪,我揣著相機與錄音,去河邊找鬼秕岛。 笑死碌燕,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的继薛。 我是一名探鬼主播修壕,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼遏考!你這毒婦竟也來了慈鸠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤灌具,失蹤者是張志新(化名)和其女友劉穎青团,沒想到半個月后譬巫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡督笆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年芦昔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娃肿。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡烟零,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出咸作,到底是詐尸還是另有隱情锨阿,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布记罚,位于F島的核電站墅诡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏桐智。R本人自食惡果不足惜末早,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望说庭。 院中可真熱鬧然磷,春花似錦、人聲如沸刊驴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捆憎。三九已至舅柜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間躲惰,已是汗流浹背致份。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留础拨,地道東北人氮块。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像诡宗,于是被迫代替她去往敵國和親滔蝉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容