Java多線程——<五>后台線程(daemon)


一、后台線程(守護線程)

  學一個東西,最重要的一點就是,為什么要用它?

  后台線程區別於普通線程,普通線程又可以稱為用戶線程,只完成用戶自己想要完成的任務,不提供公共服務。而有時,我們希望編寫一段程序,能夠提供公共的服務,保證所有用戶針對該線程的請求都能有響應。

  仔細來看下后台線程的定義:指在程序運行的時候在后台提供一種通用服務的線程,並且這種線程並不屬於程序中不可或缺的部分。

二、實現后台線程

  1.我們先定義任務及響應的線程

  定義任務:Thread.yield();讓線程暫停一段時間

class DaemonSpawn implements Runnable{
    public void run(){
        while(true)
            Thread.yield();
    }
}

  定一個線程,有一個屬性Thread[] t,用於存放子線程

class Daemon implements Runnable{
    //該任務下創建很多子線程
    private Thread[] t = new Thread[10];
    public void run(){
        //為線程池填充線程,並將所有線程啟動
        for(int i = 0 ; i < t.length ; i++){
            t[i] = new Thread(new DaemonSpawn());
            t[i].start();
            System.out.println("DaemonSpawn "+i+"started, ");
        }
        for(int i = 0 ; i < t.length ; i++){
            System.out.println("t["+i+"].isDaemon()="+t[i].isDaemon()+", ");
        }
        /*
         * Daemon進入了無線循環,並在循環里調用yield方法把控制權交給其他進程
         */
        while(true)
            Thread.yield();
    }
}

  講定義的線程設定為后台線程

  public static void main(String[] args) throws InterruptedException{
        /*
         * Daemon被設置為了后台線程,它的所有子線程也自然就是后台線程了
         */
        Thread d = new Thread(new Daemon());
        d.setDaemon(true);
        d.start();
        System.out.println("d.isDaemon()="+d.isDaemon()+",");
        TimeUnit.SECONDS.sleep(1);
    }

  至此,后台線程已定義並跑起來了。輸出結果:

DaemonSpawn 0started, 
DaemonSpawn 1started, 
DaemonSpawn 2started, 
DaemonSpawn 3started, 
DaemonSpawn 4started, 
DaemonSpawn 5started, 
DaemonSpawn 6started, 
DaemonSpawn 7started, 
DaemonSpawn 8started, 
DaemonSpawn 9started, 
t[0].isDaemon()=true, 
t[1].isDaemon()=true, 
t[2].isDaemon()=true, 
t[3].isDaemon()=true, 
t[4].isDaemon()=true, 
t[5].isDaemon()=true, 
t[6].isDaemon()=true, 
t[7].isDaemon()=true, 
t[8].isDaemon()=true, 
t[9].isDaemon()=true, 
d.isDaemon()+true,

  2.有一點要指出:所有的“非后台線程”結束時,程序也就終止了,同時會殺死進程中所有后台線程:main就是一個非后台線程

   首先,如何證明main是非后台線程,還是用是上面那段程序

    其次,如何證明非后台線程退出后,后台線程會被殺死呢?

public class TaskDaemon implements Runnable{

    @Override
    public void run() {
        try{
            while(true){
                TimeUnit.MILLISECONDS.sleep(100);
                System.out.println(Thread.currentThread()+"  "+this);
            }
        }catch(InterruptedException e){
            System.out.println("sleep() interrupted");
        }
    }
    public static void main(String[] args) throws InterruptedException{
        /*
         * 可以通過查看該程序的結果理解后台線程
         * 創建了9個線程,都聲明為后台線程,然后啟動他們,在非后台線程結束之前,后台線程會被線程調用器調用
         * main就是一個非后台線程,for循環結束之后輸出了"All daemons started"證明main快要結束了,但是你讓它睡眠了一會保證main不退出
         * 這樣后台線程就會跑着,於是有了后面的打印結果
         */
        for(int i = 0 ; i < 10 ; i++){
            //后台線程本質上也是一個線程,通過任務來創建該線程
            Thread daemon = new Thread(new TaskDaemon());
            //想將創建的線程聲明為后台線程 ,必須在啟動前將其設置為true
            daemon.setDaemon(true);
            daemon.start();
        }
        System.out.println("All daemons started");
        TimeUnit.MILLISECONDS.sleep(175);
    }
    
}

  3.通過isDaemon()方法來判斷一個線程是否是一個后台線程

   一個后台線程創建的任何線程都將被自動設置成后台線程,例如:Daemons中所示。

  4.后台線程在不執行finally字句的情況下就會終止其run()方法,例如:DaemonsDontRunFinally

class ADaemon implements Runnable{
    @Override
    public void run() {
        try{
            System.out.println("Starting ADaemon");
            TimeUnit.SECONDS.sleep(1);
        }catch(InterruptedException e){
            System.out.println("Exiting via InterruptedException");
        }finally{
            System.out.println("Thie should always run?");
        }
    }
}
public static void main(String[] args){
        //當最后一個非后台線程終止時,后台線程會“突然”終止
        //故一旦main退出,jvm就會立即關閉所有的后台進程,而不會有任何你希望出現的確認形式
        Thread t = new Thread(new ADaemon());
        //如果注釋掉下面這句話,finally將會執行
        t.setDaemon(true);
        t.start();
}

  可以看到輸出結果,finally中結果並沒有執行

三、自定義后台線程工廠

  1.自定義后台線程工廠

public class TaskDaemonFactory implements ThreadFactory{
    public Thread newThread(Runnable r){
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
    }
}

  2.創建線程時試用該工廠

     /*
         * Executors.newCachedThreadPool();方法用來接受一個ThreadFactory對象,而這個對象將被用來創建新的線程
         * 所以,你的Facotry重寫了ThreadFacotry方法之后,要去實現他的創建線程方法,方法里默認將線程聲明為后台線程
         */
        ExecutorService exec = Executors.newCachedThreadPool(new TaskDaemonFactory());
        for(int i = 0 ;i < 10 ;i++){
            exec.execute(new TaskDaemonFromFactory());//這個是一個自定義任務
        }
        System.out.println("All daemons started");
        TimeUnit.MILLISECONDS.sleep(500);

四、總結

后台線程(daemon)
  |——定義:指在程序運行的時候在后台提供一種通用服務的線程,並且這種線程並不屬於程序中不可或缺的部分
  |       |——所有的“非后台線程”結束時,程序也就終止了,同時會殺死進程中所有后台線程:main就是一個非后台線程
  |——聲明並試用后台線程
  |       |——傳統方式:通過聲明線程,操作線程來定義后台線程
  |       |         |——Thread daemon = new Thread(new TaskDaemon());//將任務交給線程也叫聲明線程
  |       |         |—— daemon.setDaemon(true);//將線程設置為后台線程
  |       |         |——daemon.start();//啟動后台線程
  |       |——由executor調用線程工廠:通過編寫定制的ThreadFactory,可以定制由Executor創建的線程的屬性
  |           |——1.實現ThreadFactory接口
  |           |——2.重寫newThread方法
  |           |—— public Thread newThread(Runnable r){
  |           |—— Thread t = new Thread(r);
  |           |—— t.setDaemon(true);
  |           |—— return t;
  |           |—— }
  |           |——3.將定制的TaskDaemonFactory傳遞給Executor,它將用此來生成對應的線程,並操縱他們
  |           |—— 每個靜態的ExecutorService創建方法都被重載為接受一個ThreadFactory對象,該對象將被用來創建新的線程
  |           |—— ExecutorService exec = Executors.newCachedThreadPool(new TaskDaemonFactory());
  |           |——4.將任務傳遞給exec,它會幫你執行線程操作
  |           |—— exec.execute(new TaskDaemonFromFactory());

注:以上代碼均來自《Thinking in java》,總結內容均是個人理解,如有錯誤請大家批評指正,謝謝

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM