使用者執行緒和守護執行緒
在Java中執行緒分為兩類:使用者執行緒(User thread)和守護執行緒(Daemon thread)。
守護程序(Daemon)是執行在後臺的一種特殊程序。它的作用是為其他執行緒的執行提供便利服務,它獨立於控制終端並且週期性地執行某種任務或等待處理某些發生的事件。也就是說守護執行緒不依賴於終端,但是依賴於系統,與系統“同生共死”。因此,當所有的非守護執行緒結束時,程式也就終止了,同時會殺死程序中的所有守護執行緒。反過來說,只要任何非守護執行緒還在執行,程式就不會終止。
垃圾回收執行緒就是一個經典的守護執行緒,當我們的程式中不再有任何執行的Thread,程式就不會再產生垃圾,垃圾回收器也就無事可做,所以當垃圾回收執行緒是
守護執行緒和使用者執行緒的沒啥本質的區別:唯一的不同之處就在於虛擬機器的離開:如果使用者執行緒已經全部退出執行了,只剩下守護執行緒存在了,虛擬機器也就退出了。 因為沒有了被守護者,守護執行緒也就沒有工作可做了,也就沒有繼續執行程式的必要了。
將執行緒轉換為守護執行緒可以通過呼叫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..(無限輸出)