用戶線程和守護線程

在Java中線程分爲兩類:用戶線程(User thread)和守護線程(Daemon thread)。

守護進程(Daemon)是運行在後臺的一種特殊進程。它的作用是爲其他線程的運行提供便利服務,它獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。也就是說守護線程不依賴於終端,但是依賴於系統,與系統同生共死因此,當所有的非守護線程結束時,程序也就終止了,同時會殺死進程中的所有守護線程。反過來說,只要任何非守護線程還在運行,程序就不會終止。

垃圾回收線程就是一個經典的守護線程,當我們的程序中不再有任何運行的Thread,程序就不會再產生垃圾,垃圾回收器也就無事可做,所以當垃圾回收線程是JVM上僅剩的線程時,垃圾回收線程會自動離開。它始終在低級別的狀態中運行,用於實時監控和管理系統中的可回收資源。

守護線程和用戶線程的沒啥本質的區別:唯一的不同之處就在於虛擬機的離開:如果用戶線程已經全部退出運行了,只剩下守護線程存在了,虛擬機也就退出了。 因爲沒有了被守護者,守護線程也就沒有工作可做了,也就沒有繼續運行程序的必要了。

將線程轉換爲守護線程可以通過調用Thread對象的setDaemon(true)方法來實現。在使用守護線程時需要注意一下幾點:

(1) thread.setDaemon(true)必須在thread.start()之前設置,否則會跑出一個IllegalThreadStateException異常。你不能把正在運行的常規線程設置爲守護線程。

(2) 在Daemon線程中產生的新線程也是Daemon的。

(3) 守護線程應該永遠不去訪問固有資源,如文件、數據庫,因爲它會在任何時候甚至在一個操作的中間發生中斷。

在父線程中建立一個子線程並將子線程設置爲守護線程,當父線程結束時,子線程也隨之結束。

如果沒有將子線程設置爲守護線程,當父線程結束時,子線程繼續運行,並沒有結束。

守護線程有一個應用場景,就是當主線程結束時,結束其餘的子線程(守護線程)自動關閉,就免去了還要繼續關閉子線程的麻煩。不過如果真有這種場景,還是用中斷的方式實現比較合理。

import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
import java.util.concurrent.TimeUnit;  
   
public class ThreadTest  
{  
   
    public static void main(String[] args)  
    {  
        Thread mainThread = new Thread(new Runnable(){  
            public void run()  
            {  
                System.out.println("主線程開始...");  
                Thread sonThread = new Thread(new Thread1(Thread.currentThread()));  
                sonThread.setDaemon(false);  
                sonThread.start();  
                   
                try 
                {  
                    TimeUnit.MILLISECONDS.sleep(10000);  
                }  
                catch (InterruptedException e)  
                {  
                    e.printStackTrace();  
                }  
                System.out.println("主線程結束");  
            }  
        });  
        mainThread.start();  
    }  
       
}  
   
class Thread1 implements Runnable  
{  
    private Thread mainThread;  
       
    public Thread1(Thread mainThread)  
    {  
        this.mainThread = mainThread;  
    }  
       
    @Override 
    public void run()  
    {  
        while(mainThread.isAlive())  
        {  
            System.out.println("子線程運行中....");  
            try 
            {  
                TimeUnit.MILLISECONDS.sleep(1000);  
            }  
            catch (InterruptedException e)  
            {  
                e.printStackTrace();  
            }  
        }  
    }  
       
}

運行結果如下:

主線程開始... 
子線程運行中.... 
子線程運行中.... 
子線程運行中.... 
子線程運行中.... 
子線程運行中.... 
子線程運行中.... 
子線程運行中.... 
子線程運行中.... 
子線程運行中.... 
子線程運行中.... 
子線程運行中.... 
主線程結束

注意:不是說當子線程是守護線程,主線程結束,子線程就跟着結束,這裏的前提條件是:當前jvm應用實例中沒有用戶線程繼續執行,如果有其他用戶線程繼續執行,那麼後臺線程不會中斷

import java.util.concurrent.TimeUnit;  
   
public class DaemonThreadTest  
{  
    public static void main(String[] args)  
    {  
        Thread mainThread = new Thread(new Runnable(){  
            @Override 
            public void run()  
            {  
                Thread childThread = new Thread(new ClildThread());  
                childThread.setDaemon(true);  
                childThread.start();  
                System.out.println("I'm main thread...");  
            }  
        });  
        mainThread.start();  
           
        Thread otherThread = new Thread(new Runnable(){  
            @Override 
            public void run()  
            {  
                while(true)  
                {  
                    System.out.println("I'm other user thread...");  
                    try 
                    {  
                        TimeUnit.MILLISECONDS.sleep(1000);  
                    }  
                    catch (InterruptedException e)  
                    {  
                        e.printStackTrace();  
                    }  
                }  
            }  
        });  
        otherThread.start();  
    }  
}  
   
class ClildThread implements Runnable  
{  
    @Override 
    public void run()  
    {  
        while(true)  
        {  
            System.out.println("I'm child thread..");  
            try 
            {  
                TimeUnit.MILLISECONDS.sleep(1000);  
            }  
            catch (InterruptedException e)  
            {  
                e.printStackTrace();  
            }  
        }  
    }  
}

運行結果如下:

I'm other user thread... 
I'm child thread.. 
I'm main thread... 
I'm other user thread... 
I'm child thread.. 
I'm other user thread... 
I'm child thread.. 
I'm child thread.. 
I'm other user thread... 
I'm other user thread... 
I'm child thread..   
I'm child thread.. 
I'm other user thread... 
I'm child thread.. 
I'm other user thread... 
I'm child thread.. 
I'm other user thread... 
I'm child thread.. 
I'm other user thread... 
I'm child thread..(無限輸出)

寫java多線程程序時,一般比較喜歡用java自帶的多線程框架,比如ExecutorService,但是java的線程池會將守護線程轉換爲用戶線程,所以如果要使用後臺線程就不能用java的線程池。
如下,線程池中將daemon線程轉換爲用戶線程的程序片段:

static class DefaultThreadFactory implements ThreadFactory {  
    private static final AtomicInteger poolNumber = new AtomicInteger(1);  
    private final ThreadGroup group;  
    private final AtomicInteger threadNumber = new AtomicInteger(1);  
    private final String namePrefix;  
   
    DefaultThreadFactory() {  
        SecurityManager s = System.getSecurityManager();  
        group = (s != null) ? s.getThreadGroup() :  
                              Thread.currentThread().getThreadGroup();  
        namePrefix = "pool-" +  
                      poolNumber.getAndIncrement() +  
                     "-thread-";  
    }  
   
    public Thread newThread(Runnable r) {  
        Thread t = new Thread(group, r,  
                              namePrefix + threadNumber.getAndIncrement(),  
                              0);  
        if (t.isDaemon())  
            t.setDaemon(false);  
        if (t.getPriority() != Thread.NORM_PRIORITY)  
            t.setPriority(Thread.NORM_PRIORITY);  
        return t;  
    }  
}

注意到,這裏不僅會將守護線程轉變爲用戶線程,而且會把優先級轉變爲Thread.NORM_PRIORITY。

import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
import java.util.concurrent.TimeUnit;  
   
public class DaemonThreadTest  
{  
    public static void main(String[] args)  
    {  
        Thread mainThread = new Thread(new Runnable(){  
            @Override 
            public void run()  
            {  
                ExecutorService exec = Executors.newCachedThreadPool();  
                Thread childThread = new Thread(new ClildThread());  
                childThread.setDaemon(true);  
                exec.execute(childThread);  
                exec.shutdown();  
                System.out.println("I'm main thread...");  
            }  
        });  
        mainThread.start();  
    }  
}  
   
class ClildThread implements Runnable  
{  
    @Override 
    public void run()  
    {  
        while(true)  
        {  
            System.out.println("I'm child thread..");  
            try 
            {  
                TimeUnit.MILLISECONDS.sleep(1000);  
            }  
            catch (InterruptedException e)  
            {  
                e.printStackTrace();  
            }  
        }  
    }  
}

運行結果如下:

I'm main thread...
I'm child thread..
I'm child thread..
I'm child thread..
I'm child thread..
I'm child thread..
I'm child thread..(無限輸出)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章