【Java并發(fā)編程】線程的狀態(tài)及基本操作

概述

先了解一下基本概念。線程是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位妆绞。它被包含在進(jìn)程中板惑,是進(jìn)程中的實(shí)際運(yùn)作單位。一條線程指的是進(jìn)程中一個(gè)單一順序的控制流端礼,一個(gè)進(jìn)程中可以并發(fā)多個(gè)線程禽笑,每條線程并行執(zhí)行不同的任務(wù)(多核CPU下才能實(shí)現(xiàn)線程并行)。在單核CPU中蛤奥,多線程的并發(fā)從宏觀角度看佳镜,是多個(gè)線程同時(shí)執(zhí)行,但是從微觀角度看凡桥,多線程還是需要通過CPU的時(shí)間片切換來實(shí)現(xiàn)的蟀伸,同一時(shí)間是無法做到多個(gè)線程在單個(gè)CPU中執(zhí)行的。在多核CPU中唬血,才能實(shí)現(xiàn)多個(gè)線程并行執(zhí)行望蜡。在實(shí)際應(yīng)用場景中,具體使用單線程還是多線程拷恨, 需要根據(jù)實(shí)際場景來做衡量脖律,并不是所有場景都更適合多線程。

新建線程的幾種方式

Java中新建線程有三種方式:繼承Tread類腕侄;實(shí)現(xiàn)Runnable接口小泉;通過callable和Future實(shí)現(xiàn);

繼承Thread

  1. 定義Thread類的子類冕杠,并重寫run方法微姊,該類中run方法的方法體就代表了該線程要執(zhí)行的內(nèi)容。
  2. 創(chuàng)建Thread子類的實(shí)例分预,即創(chuàng)建線程對象兢交。
  3. 調(diào)用線程對象的start()方法啟動(dòng)線程。
    public static void main(String[] args) {
        //第一種創(chuàng)建線程實(shí)例的方式
        Thread threadDemo1 = new ThreadDemo1();
        threadDemo1.start();
        //第二種創(chuàng)建線程實(shí)例的方式
        Thread threadDemo2 = new Thread(){
            @Override
            public void run(){
                System.out.println("新建線程Demo2笼痹!");
            }
        };
        threadDemo2.start();
    }

    static class ThreadDemo1 extends Thread{

        @Override
        public void run() {
            System.out.println("繼承Thread類配喳,新建線程Demo1酪穿!");
        }
    }

實(shí)現(xiàn)Runnable

  1. 實(shí)現(xiàn)Runnable接口,重寫該接口的run()方法晴裹。
  2. 創(chuàng)建Runnable實(shí)現(xiàn)類的實(shí)例被济,并將此實(shí)例作為創(chuàng)建Thread的Target來創(chuàng)建thread的實(shí)例,該tread實(shí)例才是真正的線程象涧团。
  3. 調(diào)用thread實(shí)例的start()方法啟動(dòng)線程只磷。
public static void main(String[] args) {
        //第一種實(shí)現(xiàn)方式
        Runnable runnable = new RunnableDemo1();
        Thread runnableDemo1 = new Thread(runnable);
        runnableDemo1.start();
        //第二種實(shí)現(xiàn)方式
        Thread runnableDemo2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"實(shí)現(xiàn)Runnable接口,新建RunnableDemo2!");
            }
        });
        runnableDemo2.start();
    }

    static class RunnableDemo1 implements  Runnable {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "實(shí)現(xiàn)Runnable接口泌绣,新建線程RunnableDemo1!");
        }
    }

通過Callable和Future

  1. 實(shí)現(xiàn)Callable接口钮追,并重寫改接口的call()方法,call()方法的方法體即該類的執(zhí)行內(nèi)容赞别。
  2. 創(chuàng)建Callable接口實(shí)現(xiàn)類的實(shí)例畏陕,并使用FutureTask來包裝callable實(shí)例,該FutureTask封裝了callable實(shí)例的call()方法的返回值仿滔。(FutureTask是一個(gè)包裝器惠毁,它通過接受Callable來創(chuàng)建,它同時(shí)實(shí)現(xiàn)了Future和Runnable接口崎页。)
  3. 使用FutureTask實(shí)例作為thread的target創(chuàng)建線程鞠绰。
  4. 調(diào)用tread的start()方法啟動(dòng)線程。
public static void main(String[] args) {
        CallableDemo1 callableDemo1 = new CallableDemo1();
        FutureTask<Integer> futureTask = new FutureTask<>(callableDemo1);
        Thread thread = new Thread(futureTask);
        thread.start();
        try {
            Integer i = futureTask.get();
            System.out.println(Thread.currentThread().getName() + "獲取到線程的返回值為:" + i);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }


    static class CallableDemo1 implements Callable<Integer>{

        @Override
        public Integer call() throws Exception {
            int i = 100;
            System.out.println(Thread.currentThread().getName() +" " + i);
            return i;
        }
    }

三種創(chuàng)建方式的比較

  • 由于Java是單繼承多實(shí)現(xiàn)的飒焦,所以盡量使用接口實(shí)現(xiàn)的方式創(chuàng)建線程蜈膨,這樣還可以繼承其余的類
  • callable接口實(shí)現(xiàn)方式,較其余兩種相對復(fù)雜牺荠,但是該實(shí)現(xiàn)方式線程執(zhí)行后有返回值翁巍,其余方式?jīng)]有
  • Thread類實(shí)現(xiàn)了Runnable接口

線程狀態(tài)的轉(zhuǎn)換

我們通過查看Thread類的源碼,發(fā)現(xiàn)線程只有六種狀態(tài):NEW(新建)休雌、RUNNABLE(運(yùn)行)灶壶、BLOCKED(阻塞裝填)、WAITING(等待狀態(tài))杈曲、TIMED_WAITING(超時(shí)等待狀態(tài))驰凛、TERMINATED(終止?fàn)顟B(tài)),具體源碼如下:

public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

從源代碼注釋中可以整理出線程狀態(tài)轉(zhuǎn)換的過程。當(dāng)一個(gè)線程創(chuàng)建之后,就處于NEW(新建狀態(tài)),調(diào)用Thread.start()后蕴侧,線程并不會(huì)立馬執(zhí)行,而是進(jìn)入REDAY(就緒狀態(tài))遭顶,等待系統(tǒng)調(diào)度該線程之后,進(jìn)入RUNNING(運(yùn)行中狀態(tài))锻梳。
CPU執(zhí)行每個(gè)線程是有一個(gè)時(shí)間限制的伍茄,這個(gè)時(shí)間段被稱為時(shí)間片间唉,當(dāng)一個(gè)時(shí)間片結(jié)束后绞灼,線程仍在執(zhí)行中利术,那么系統(tǒng)會(huì)將線程重新置為REDAY(就緒狀態(tài)),或者在線程運(yùn)行時(shí)呈野,調(diào)用Thread.yeild()方法,該線程一樣會(huì)被置為READY狀態(tài)印叁,等待系統(tǒng)再次調(diào)用被冒。當(dāng)系統(tǒng)調(diào)用Object.wait()、Thread.join()轮蜕、LockSupport.park()方法后昨悼,線程狀態(tài)轉(zhuǎn)換為WAITTING(等待狀態(tài))。而同樣的跃洛,如果調(diào)用的是Object.wait(long)率触、Thread.sleep(long)、Thread.join(long)汇竭、LockSupport.parkNanos()葱蝗、LockSupport.parkUntil()方法時(shí),會(huì)進(jìn)入TIMED_WAITING(超時(shí)等待)狀態(tài)细燎。當(dāng)線程進(jìn)入WAITING(等待狀態(tài))后需要系統(tǒng)調(diào)用Object.notify()两曼、Object.notifyAll()、LockSupport.unPark(Thread)方法才能重新喚醒線程玻驻。當(dāng)線程進(jìn)入TIMED_WAITING(超時(shí)等待)狀態(tài)后當(dāng)?shù)却龝r(shí)間超過long值后悼凑,線程會(huì)自動(dòng)喚醒,或者調(diào)用Object.notify()璧瞬、Object.notifyAll()户辫、LockSupport.unPark(Thread)后也會(huì)喚醒線程。當(dāng)線程在READY或RUNNING狀態(tài)中嗤锉,等待進(jìn)入synchronized方法或代碼塊的時(shí)候渔欢,即沒有獲取到對象鎖的時(shí)候,就將進(jìn)入阻塞狀態(tài)档冬,直到該線程獲取到對象鎖之后膘茎,重新進(jìn)入READY狀態(tài)。當(dāng)線程運(yùn)行結(jié)束后酷誓,進(jìn)入TERMINATED(線程終止)狀態(tài)披坏。

將上述過程總結(jié)之后,可以用下圖表示:

線程狀態(tài)轉(zhuǎn)換.jpg

這里需要注意幾點(diǎn):

  1. 處于WAITING和TIMED_WAITING的線程也可能持有對象鎖盐数,比如調(diào)用Thread.sleep()方法進(jìn)入等待狀態(tài)的線程就有可能持有對象鎖棒拂。
  2. 當(dāng)線程遇到I/O的時(shí)候,還是處于RUNNABLE狀態(tài)

線程常用方法

sleep()方法

sleep()是Thread類的靜態(tài)方法,源碼如下:

    public static native void sleep(long millis) throws InterruptedException;

它是native修飾的靜態(tài)方法帚屉,用于讓當(dāng)前線程按照指定的long值時(shí)間進(jìn)行休眠谜诫,其休眠時(shí)間的精度取決于處理器的計(jì)時(shí)器和調(diào)度器,在休眠指定時(shí)間后攻旦,線程會(huì)恢復(fù)執(zhí)行喻旷。需要注意的是sleep()方法會(huì)交出CPU,但是不會(huì)釋放對象鎖牢屋。從上面的線程狀態(tài)轉(zhuǎn)換圖中且预,可以看到sleep方法會(huì)使線程進(jìn)入TIMED_WAITTING狀態(tài)。Thread.sleep(long)方法經(jīng)常被拿來與Object.wait()方法進(jìn)行比較烙无,兩者的主要區(qū)別如下:

  1. sleep()方法為Thread類的靜態(tài)方法锋谐。而wait()方法為Object類的實(shí)例方法。
  2. wait()方法必須在synchronized修飾的同步塊或同步方法中調(diào)用截酷,否則會(huì)報(bào)InterruptedException異常涮拗,換言之,wait()方法調(diào)用必須持有對象鎖迂苛。而sleep方法則沒有這個(gè)限制,可以在任何地方使用三热。
  3. wait()方法會(huì)釋放CPU資源,并釋放對象鎖灾部,等待下次重新獲取資源康铭。而sleep()方法,只會(huì)釋放CPU資源赌髓,但是不會(huì)釋放對象鎖从藤。
  4. 調(diào)用sleep()方法的線程超過等待時(shí)間后,獲取到CPU時(shí)間片資源則會(huì)立即執(zhí)行锁蠕。而調(diào)用了wait()方法的線程必須等待Object.notify(),Object.notifyAll()之后夷野,才會(huì)嘗試重新獲取CPU資源,并執(zhí)行荣倾。

yield()方法

yield()也是Thread類的靜態(tài)方法悯搔,源碼如下:

    public static native void yield();

當(dāng)執(zhí)行該方法時(shí),線程會(huì)讓出CPU舌仍,進(jìn)入就緒狀態(tài)妒貌。但需要注意的是,讓出CPU并不代表該線程不執(zhí)行了铸豁,當(dāng)前線程仍然會(huì)參與到下一次CPU時(shí)間片的競爭中灌曙,如果該線程在下一次競爭時(shí),仍然獲取到了CPU节芥,那么該線程會(huì)繼續(xù)執(zhí)行在刺。另外逆害,讓出的CPU時(shí)間片只允許與它相同優(yōu)先級(jí)的線程去競爭。下面說一下線程優(yōu)先級(jí)是個(gè)什么東西◎纪眨現(xiàn)代操作系統(tǒng)中基本采用時(shí)分的形式調(diào)度運(yùn)行的線程魄幕,操作系統(tǒng)會(huì)分出一個(gè)個(gè)時(shí)間片,線程會(huì)分配到若干時(shí)間片颖杏,當(dāng)線程分配到的時(shí)間片用完纯陨,就會(huì)發(fā)生線程調(diào)度,該線程只能等待下一次分配输玷。線程分配到的處理器時(shí)間多少也就決定了線程使用處理器資源的多少队丝,而線程優(yōu)先級(jí)就是決定線程分配時(shí)間多少的線程屬性。在Java中欲鹏,線程通過Thread中的一個(gè)int型私有成員變量來Priority(private int priority;)來控制線程優(yōu)先級(jí),優(yōu)先級(jí)的范圍從1~10臭墨,可以在構(gòu)建線程的時(shí)候通過調(diào)用setPriority(priority)來設(shè)置赔嚎,默認(rèn)優(yōu)先級(jí)為5,優(yōu)先級(jí)高的線程相較于優(yōu)先級(jí)低的線程先獲取到CPU的時(shí)間片胧弛。具體源碼如下:

public final static int MIN_PRIORITY = 1;

public final static int NORM_PRIORITY = 5;

public final static int MAX_PRIORITY = 10;

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);
        }
    }

但是有一點(diǎn)需要注意尤误,不同JVM以及不同操作系統(tǒng)上,對線程的規(guī)劃是存在差異的结缚,有的操作系統(tǒng)甚至?xí)雎跃€程優(yōu)先級(jí)的設(shè)定损晤。yield()方法與sleep()方法一樣會(huì)釋放CPU資源,但是不會(huì)釋放對象鎖(如果當(dāng)前線程持有對象鎖的話)红竭;它們之間不同的是sleep()釋放的CPU資源所有線程都可以競爭尤勋,但是yield()釋放的資源只有相同優(yōu)先級(jí)的線程才能競爭

join()方法

如果在一個(gè)線程實(shí)例A中調(diào)用了threadB.join()方法,那么當(dāng)前線程A會(huì)等待線程B終止后才會(huì)繼續(xù)執(zhí)行茵宪。其在Thread類中的源碼如下:

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;
            }
        }
    }

    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 void join() throws InterruptedException {
        join(0);
    }

可以看到Thread中除了提供join()方法外最冰,還提供了超時(shí)等待相關(guān)的join(long)和join(long,int)方法。如果線程B超過給定時(shí)間還未執(zhí)行完成稀火,那么線程A會(huì)在線程B執(zhí)行超時(shí)后繼續(xù)執(zhí)行暖哨。翻看源碼發(fā)現(xiàn)join()和join(long,int)最終都是調(diào)用join(long)方法,而join(long)方法中多次調(diào)用了isAlive()方法凰狞。

public final native boolean isAlive();

該方法為native修飾的本地方法篇裁,該方法用于判斷一個(gè)線程是否存活∩娜簦可以看出來當(dāng)前等待對象threadA會(huì)一直阻塞达布,直到被等待對象threadB結(jié)束后即isAlive()返回false的時(shí)候才會(huì)結(jié)束while循環(huán),當(dāng)threadB退出時(shí)會(huì)調(diào)用notifyAll()方法通知所有的等待線程

下面來寫一個(gè)例子斩熊,看下join的作用

public static void main(String[] args) {
        Thread preThread = Thread.currentThread();
        for(int i= 0;i<10;i++){
            Thread joinDemo = new JoinDemo(i,preThread);
            joinDemo.start();
            preThread = joinDemo;
        }
    }

    static class JoinDemo extends Thread{
        private int i;
        private Thread preThread;

        public JoinDemo(int i,Thread preThread){
            this.i = i;
            this.preThread = preThread;
        }

        @Override
        public void run(){
            try {
                preThread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(this.getName()+": "+ i);
        }
    }

這段代碼往枣,去除掉run()方法中的join方法調(diào)用的時(shí)候,答應(yīng)出的i順序是不確定的。但是加上join后分冈,即相當(dāng)于每個(gè)線程的執(zhí)行需要等待上一個(gè)線程執(zhí)行結(jié)束圾另,那么i就會(huì)按照自然順序打印到控制臺(tái),得到結(jié)果如下:

加join()方法前:            加join()方法后:
Thread-1: 1                 Thread-0: 0
Thread-0: 0                 Thread-1: 1
Thread-2: 2                 Thread-2: 2
Thread-3: 3                 Thread-3: 3
Thread-4: 4                 Thread-4: 4
Thread-5: 5                 Thread-5: 5
Thread-6: 6                 Thread-6: 6
Thread-7: 7                 Thread-7: 7
Thread-8: 8                 Thread-8: 8
Thread-9: 9                 Thread-9: 9

interrupt()方法

interrupt()方法為中斷線程方法雕沉,調(diào)用該方法并不是里面中斷線程集乔,而是將線程中的中斷標(biāo)志位設(shè)置為true;中斷好比其他線程對該線程打了一個(gè)招呼,其他線程可以調(diào)用該線程的interrupt()方法對其進(jìn)行中斷操作,而中斷的結(jié)果線程是死亡、還是等待新的任務(wù)或是繼續(xù)運(yùn)行至下一步坡椒,就取決于這個(gè)程序本身扰路。該線程可以調(diào)用isInterrupted()來感知其他線程對其自身的中斷操作,從而做出響應(yīng)倔叼。也可以調(diào)用Interrupted()方法來獲取中斷標(biāo)志位狀態(tài)汗唱,但是該方法獲取到標(biāo)志位狀態(tài)后,會(huì)將標(biāo)志位重新設(shè)置為false丈攒。需要注意的是哩罪,當(dāng)拋出InterruptedException時(shí)候,會(huì)清除中斷標(biāo)志位巡验,也就是說在調(diào)用isInterrupted會(huì)返回false际插。

方法名 詳細(xì)解釋 備注
interrupt() 中斷該線程對象 如果該線程被調(diào)用Object.wait()/wait(long)
或調(diào)用了Thread.sleep(long)/join()/join(long)方法時(shí),
會(huì)拋出InterruptedException異常,并清除中斷標(biāo)志位
isInterrupted() 檢測該線程是否被中斷 中斷標(biāo)志位不會(huì)被清除显设,類似于get()方法
interrupted() 檢測該線程是否被中斷 中斷標(biāo)志位會(huì)被清除框弛,類似于get()+set()方法

下面我們結(jié)合具體的例子來看下:

public class InterruptDemo {
    public static void main(String[] args) throws InterruptedException {
        //sleepThread睡眠1000ms
        final Thread sleepThread = new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                super.run();
            }
        };
        //busyThread一直執(zhí)行死循環(huán)
        Thread busyThread = new Thread() {
            @Override
            public void run() {
                while (true) ;
            }
        };
        sleepThread.start();
        busyThread.start();
        sleepThread.interrupt();
        busyThread.interrupt();
        while (sleepThread.isInterrupted()) ;
        System.out.println("sleepThread isInterrupted: " + sleepThread.isInterrupted());
        System.out.println("busyThread isInterrupted: " + busyThread.isInterrupted());
    }
}

最終輸出結(jié)果如下:

sleepThread isInterrupted: false
busyThread isInterrupted: true
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at com.xcy.javaConcurrent.InterruptDemo$1.run(InterruptDemo.java:14)


以上demo中開啟了兩個(gè)線程分別為sleepThread和BusyThread。sleepThread調(diào)用sleep方法睡眠1秒捕捂,busyThread則進(jìn)行死循環(huán)瑟枫。當(dāng)分別對兩個(gè)線程進(jìn)行interrupt操作時(shí),可以看出sleepThread拋出InterruptedException绞蹦,并清除了標(biāo)志位力奋。而busyThread則沒有清除標(biāo)志位。這里我們關(guān)注一下while (sleepThread.isInterrupted()) ;這行代碼幽七,Interrupt()方法也可以看做線程之間的一種簡單交互方式景殷,這行代碼說明main方法會(huì)一直監(jiān)控sleepThread的中斷標(biāo)志位狀態(tài),當(dāng)中斷標(biāo)志位被清零時(shí)才會(huì)繼續(xù)往下執(zhí)行澡屡。

守護(hù)線程 isDaemon()方法###

Thread中的isDaemon方法用于判斷該線程是否為守護(hù)線程猿挚,守護(hù)線程是運(yùn)行在后臺(tái)的一種特殊進(jìn)程,它獨(dú)立于控制終端驶鹉,并且周期性地執(zhí)行某種任務(wù)或著等待處理某些發(fā)生的事件绩蜻。也就是在程序運(yùn)行的時(shí)候在后臺(tái)提供一種通用服務(wù)的線程,在沒有用戶線程客服務(wù)時(shí)會(huì)自動(dòng)離開室埋。用戶線程完全結(jié)束后就意味著整個(gè)系統(tǒng)的業(yè)務(wù)任務(wù)全部結(jié)束了办绝,因此系統(tǒng)就沒有對象需要守護(hù)的了伊约,守護(hù)線程自然而然就會(huì)退。當(dāng)一個(gè)Java應(yīng)用孕蝉,只有守護(hù)線程的時(shí)候屡律,虛擬機(jī)就會(huì)自然退出。例如垃圾回收線程降淮,JIT線程就可以理解守護(hù)線程超埋。
其在Thread中的源碼如下:

    public final boolean isDaemon() {
            return daemon;
        }

注:本文參考:http://www.reibang.com/p/f65ea68a4a7f ,該文章作者有一系列關(guān)于java并發(fā)包知識(shí)的講解佳鳖,值得學(xué)習(xí)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末霍殴,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子系吩,更是在濱河造成了極大的恐慌来庭,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淑玫,死亡現(xiàn)場離奇詭異巾腕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)絮蒿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叁鉴,“玉大人土涝,你說我怎么就攤上這事』夏梗” “怎么了但壮?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長常侣。 經(jīng)常有香客問我蜡饵,道長,這世上最難降的妖魔是什么胳施? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任溯祸,我火速辦了婚禮,結(jié)果婚禮上舞肆,老公的妹妹穿的比我還像新娘焦辅。我一直安慰自己,他們只是感情好椿胯,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布筷登。 她就那樣靜靜地躺著,像睡著了一般哩盲。 火紅的嫁衣襯著肌膚如雪前方。 梳的紋絲不亂的頭發(fā)上狈醉,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音惠险,去河邊找鬼苗傅。 笑死,一個(gè)胖子當(dāng)著我的面吹牛莺匠,可吹牛的內(nèi)容都是我干的金吗。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼趣竣,長吁一口氣:“原來是場噩夢啊……” “哼摇庙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起遥缕,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬榮一對情侶失蹤卫袒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后单匣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體夕凝,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年户秤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了码秉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鸡号,死狀恐怖转砖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鲸伴,我是刑警寧澤府蔗,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站汞窗,受9級(jí)特大地震影響姓赤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜仲吏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一不铆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蜘矢,春花似錦狂男、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至舞吭,卻和暖如春泡垃,著一層夾襖步出監(jiān)牢的瞬間析珊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來泰國打工蔑穴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留忠寻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓存和,卻偏偏與公主長得像奕剃,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子捐腿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

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

  • 原創(chuàng)文章&經(jīng)驗(yàn)總結(jié)&從校招到A廠一路陽光一路滄桑 詳情請戳www.codercc.com 在上一篇文章中 談到了為...
    你聽___閱讀 20,037評(píng)論 12 62
  • 林炳文Evankaka原創(chuàng)作品纵朋。轉(zhuǎn)載自http://blog.csdn.net/evankaka 本文主要講了ja...
    ccq_inori閱讀 645評(píng)論 0 4
  • ??一個(gè)任務(wù)通常就是一個(gè)程序,每個(gè)運(yùn)行中的程序就是一個(gè)進(jìn)程茄袖。當(dāng)一個(gè)程序運(yùn)行時(shí)操软,內(nèi)部可能包含了多個(gè)順序執(zhí)行流,每個(gè)順...
    OmaiMoon閱讀 1,662評(píng)論 0 12
  • 在上一篇文章中并發(fā)編程的優(yōu)缺點(diǎn)談到了為什么花功夫去學(xué)習(xí)并發(fā)編程的技術(shù)宪祥,也就是說我們必須了解到并發(fā)編程的優(yōu)缺點(diǎn)聂薪,我們...
    kevin0016閱讀 476評(píng)論 0 1
  • 青春藏澳,是葳蕤絢爛的夏花! 青春耀找,是悠揚(yáng)動(dòng)人的歡歌笆载! 曾經(jīng)的朋友,如今的陌生人涯呻。 曾經(jīng),我總以為只要是認(rèn)識(shí)...
    interested小小銳閱讀 570評(píng)論 3 5