第二篇 Java并發(fā)編程之Thread API介紹

在Java中,一般創(chuàng)建線程的兩種方式是:繼承Thread類和實(shí)現(xiàn)Runnable接口倔叼。本篇將介紹一下Thread類中相關(guān)的API的作用以及用法周伦。

1.currentThread方法
        public static native Thread currentThread();

currentThread()方法返回的是對當(dāng)前正在執(zhí)行線程對象的引用

2. sleep方法
        public static native void sleep(long millis) throws InterruptedException;
        public static void sleep(long millis, int nanos) throws InterruptedException

sleep()方法會使當(dāng)前線程進(jìn)入指定毫秒數(shù)的休眠,暫停執(zhí)行投慈,雖然給定了一個休眠的時間,但是最終要以系統(tǒng)的定時器和調(diào)度器的精度為準(zhǔn)冠骄,在休眠期間伪煤,該線程不會放棄monitor對象鎖的所有權(quán),這一點(diǎn)不同于wait()方法凛辣。sleep的用法:Thread.sleep(1000L)

在JDK1.5之后抱既,引入了一個枚舉類TimeUnit,其對sleep進(jìn)行了封裝扁誓,使用該類可以省去時間單位換算的步驟防泵,比如想實(shí)現(xiàn)對線程休眠3小時30分30秒66毫秒,使用如下方式即可:

        TimeUnit.HOURS.sleep(3);//小時
        TimeUnit.MINUTES.sleep(30);// 分鐘
        TimeUnit.SECONDS.sleep(30);// 秒
        TimeUnit.MILLISECONDS.sleep(66);// 毫秒

TimeUnit對sleep分裝的方法如下:

       public void sleep(long timeout) throws InterruptedException {
        if (timeout > 0) {
            long ms = toMillis(timeout);
            int ns = excessNanos(timeout, ms);
            Thread.sleep(ms, ns);
        }
    }
3. yield方法
        public static native void yield();

yield方法暫停當(dāng)前執(zhí)行的線程對象跋理,并執(zhí)行其他線程择克。這個暫停是會放棄CPU資源的,并且放棄CPU的時間不確定前普,有可能剛放棄肚邢,就獲得CPU資源了,也有可能放棄好一會兒拭卿,才會被CPU執(zhí)行骡湖,調(diào)用yield方法會使當(dāng)前線程從RUNNING狀態(tài)切換到RUNNABLE狀態(tài)

4. 線程interrupt方法
    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }
    調(diào)用如下方法會使得當(dāng)前線程進(jìn)入阻塞狀態(tài),而調(diào)用當(dāng)前線程的interrupt方法峻厚,即响蕴,上面的interrupt方法就可以打斷阻塞,
    使線程阻塞的方法:
    Object的wait方法以及重載的的方法
    Thread的sleep方法以及對應(yīng)的重載方法
    Thread的join方法以及對應(yīng)的重載方法
    =========================================
    // 這是一個靜態(tài)方法惠桃,但和isInterrupted()方法有很大區(qū)別浦夷,該方法會直接擦除線程的interrupt標(biāo)識
    // 需要注意的是辖试,如果當(dāng)前線程被打斷了,那么第一次調(diào)用interrupted方法會返回true劈狐,并且立即擦除interrupt標(biāo)識
    // 第二次以及之后的調(diào)用返回都是false罐孝,除非在此期間線程再一次被打斷
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

     =========================================
    //主要判斷當(dāng)前線程是否被中斷,該方法僅僅是對interrupt標(biāo)識的一個判斷肥缔,并不會影響標(biāo)識發(fā)生任何該表
    public boolean isInterrupted() {
        return isInterrupted(false);
    }

    =========================================
    // 本地方法 interrupted方法和isInterrupted方法底層都是調(diào)用的這個方法
    private native boolean isInterrupted(boolean ClearInterrupted);

5. 獲取線程ID
    public long getId() {
        return tid;
    }

在一個Java應(yīng)用中莲兢,有一個long型的全局唯一的線程ID生成器threadSeqNumber,每new出來一個線程都會把這個自增一次续膳,并賦予線程的tid屬性改艇,這個是Thread自己做的。如下坟岔,在線程初始化的方法中進(jìn)行設(shè)置:

         /* Set thread ID */
        tid = nextThreadID();// Thread 的init方法
      
        // 生成線程ID的方法
        private static synchronized long nextThreadID() {
              return ++threadSeqNumber;
        }
6. 設(shè)置和獲取優(yōu)先級
    // 獲取線程優(yōu)先級
    public final int getPriority() {
        return priority;
    }

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

一般情況下谒兄,我們是不會用到線程優(yōu)先級的,或者說社付,我們一直在使用默認(rèn)的線程優(yōu)先級5舵变,而優(yōu)先級的范圍是1~10。越大代表優(yōu)先級越高瘦穆。優(yōu)先級高的線程得到的CPU資源比較多,設(shè)置優(yōu)先級有助于幫"線程規(guī)劃器"確定下一次選擇哪一個線程優(yōu)先執(zhí)行赊豌。換句話說扛或,兩個在等待CPU的線程,優(yōu)先級高的線程越容易被CU選擇執(zhí)行碘饼,但這并不意味著優(yōu)先級低的線程就會等待優(yōu)先級高的線程先執(zhí)行完再執(zhí)行熙兔。所以,不要在程序設(shè)計(jì)中企圖使用線程優(yōu)先級綁定某些特定的業(yè)務(wù)艾恼,或者讓業(yè)務(wù)嚴(yán)重依賴線程的優(yōu)先級住涉,這可能達(dá)不到你的預(yù)期。

7. 設(shè)置線程上下文類加載器
    //獲取線程上下文類加載器钠绍,獲取這個線程是由哪個類加載器加載的舆声,如果是沒有修改線程上下文類加載器的情況下,則保持與父線程同樣的類加載器
    public ClassLoader getContextClassLoader() {
        if (contextClassLoader == null)
            return null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            ClassLoader.checkClassLoaderPermission(contextClassLoader,
                                                   Reflection.getCallerClass());
        }
        return contextClassLoader;
    }

    // 設(shè)置線程上下文類加載器:制定類加載器加載
    public void setContextClassLoader(ClassLoader cl) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("setContextClassLoader"));
        }
        contextClassLoader = cl;
    }
8. 線程的join方法

Thread類中提供了三個join方法柳爽,最終使用的是第三個join方法媳握,如下:

    public final void join() throws InterruptedException {
        join(0);
    }

    ===================================================================

    public final synchronized void join(long millis, int nanos)
    throws InterruptedException {

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        join(millis);
    }

     ===================================================================

    public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

由上面的源碼可以看出,在使用join時磷脯,調(diào)用的是wait方法去阻塞線程蛾找,而我們知道,wait方法會釋放對象鎖赵誓,這是join方法和sleep方法的不同之處打毛。join方法可以理解為柿赊,線程排隊(duì)執(zhí)行,比如有A幻枉、B碰声、C三個線程,分別執(zhí)行了join方法之后展辞,會以此執(zhí)行奥邮,代碼如下:

public class ThreadStu {

    public static void main(String[] args) throws Exception {
        // join
        join();
    }

    private static void join() throws InterruptedException {
        for (int i = 0; i < 2; i++) {
            Thread thread = new Thread(() -> {
                for (int j = 0; j < 3; j++) {
                    try {
                        System.out.println(Thread.currentThread().getName() + "#" + j);
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            thread.start();
            thread.join();
        }
    }
}

執(zhí)行結(jié)果如下:

Thread-0#0
Thread-0#1
Thread-0#2
Thread-1#0
Thread-1#1
Thread-1#2
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市罗珍,隨后出現(xiàn)的幾起案子洽腺,更是在濱河造成了極大的恐慌,老刑警劉巖覆旱,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蘸朋,死亡現(xiàn)場離奇詭異,居然都是意外死亡扣唱,警方通過查閱死者的電腦和手機(jī)藕坯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來噪沙,“玉大人炼彪,你說我怎么就攤上這事≌撸” “怎么了辐马?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長局义。 經(jīng)常有香客問我喜爷,道長,這世上最難降的妖魔是什么萄唇? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任檩帐,我火速辦了婚禮,結(jié)果婚禮上另萤,老公的妹妹穿的比我還像新娘湃密。我一直安慰自己,他們只是感情好四敞,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布勾缭。 她就那樣靜靜地躺著,像睡著了一般目养。 火紅的嫁衣襯著肌膚如雪俩由。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天癌蚁,我揣著相機(jī)與錄音幻梯,去河邊找鬼兜畸。 笑死,一個胖子當(dāng)著我的面吹牛碘梢,可吹牛的內(nèi)容都是我干的咬摇。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼煞躬,長吁一口氣:“原來是場噩夢啊……” “哼肛鹏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起恩沛,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤在扰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后雷客,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芒珠,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年搅裙,在試婚紗的時候發(fā)現(xiàn)自己被綠了皱卓。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡部逮,死狀恐怖娜汁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情兄朋,我是刑警寧澤存炮,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站蜈漓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏宫盔。R本人自食惡果不足惜融虽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望灼芭。 院中可真熱鬧有额,春花似錦、人聲如沸彼绷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽寄悯。三九已至萤衰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間猜旬,已是汗流浹背脆栋。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工倦卖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人椿争。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓怕膛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親秦踪。 傳聞我的和親對象是個殘疾皇子褐捻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評論 2 355