Daemon的作用是爲其他線程的運行提供便利服務,比如垃圾回收線程就是一個很稱職的守護者。User和Daemon兩者幾乎沒有區別,唯一的不同之處就在於虛擬機的離開:如果 User Thread已經全部退出運行了,只剩下Daemon Thread存在了,虛擬機也就退出了。 因爲沒有了被守護者,Daemon也就沒有工作可做了,也就沒有繼續運行程序的必要了。
值得一提的是,守護線程並非只有虛擬機內部提供,用戶在編寫程序時也可以自己設置守護線程。下面的方法就是用來設置守護線程的。
public final void setDaemon(boolean on)
這裏有幾點需要注意:
(1) thread.setDaemon(true)必須在thread.start()之前設置,否則會跑出一個IllegalThreadStateException異常。你不能把正在運行的常規線程設置爲守護線程。
(2) 在Daemon線程中產生的新線程也是Daemon的。
(3) 不要認爲所有的應用都可以分配給Daemon來進行服務,比如讀寫操作或者計算邏輯。
因爲你不可能知道在所有的User完成之前,Daemon是否已經完成了預期的服務任務。一旦User退出了,可能大量數據還沒有來得及讀入或寫出,計算任務也可能多次運行結果不一樣。這對程序是毀滅性的。造成這個結果理由已經說過了:一旦所有User Thread離開了,虛擬機也就退出運行了。
- //完成文件輸出的守護線程任務
- import java.io.*;
- class TestRunnable implements Runnable{
- public void run(){
- try{
- Thread.sleep(1000);//守護線程阻塞1秒後運行
- File f=new File("daemon.txt");
- FileOutputStream os=new FileOutputStream(f,true);
- os.write("daemon".getBytes());
- }
- catch(IOException e1){
- e1.printStackTrace();
- }
- catch(InterruptedException e2){
- e2.printStackTrace();
- }
- }
- }
- public class TestDemo2{
- public static void main(String[] args) throws InterruptedException
- {
- Runnable tr=new TestRunnable();
- Thread thread=new Thread(tr);
- thread.setDaemon(true); //設置守護線程
- thread.start(); //開始執行分進程
- }
- }
- //運行結果:文件daemon.txt中沒有"daemon"字符串。
看到了吧,把輸入輸出邏輯包裝進守護線程多麼的可怕,字符串並沒有寫入指定文件。原因也很簡單,直到主線程完成,守護線程仍處於1秒的阻塞狀態。這個時候主線程很快就運行完了,虛擬機退出,Daemon停止服務,輸出操作自然失敗了。
例子2 :
- public class Test {
- public static void main(String args) {
- Thread t1 = new MyCommon();
- Thread t2 = new Thread(new MyDaemon());
- t2.setDaemon(true); //設置爲守護線程
- t2.start();
- t1.start();
- }
- }
- class MyCommon extends Thread {
- public void run() {
- for (int i = 0; i < 5; i++) {
- System.out.println("線程1第" + i + "次執行!");
- try {
- Thread.sleep(7);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- class MyDaemon implements Runnable {
- public void run() {
- for (long i = 0; i < 9999999L; i++) {
- System.out.println("後臺線程第" + i + "次執行!");
- try {
- Thread.sleep(7);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
後臺線程第0次執行!
線程1第0次執行!
線程1第1次執行!
後臺線程第1次執行!
後臺線程第2次執行!
線程1第2次執行!
線程1第3次執行!
後臺線程第3次執行!
線程1第4次執行!
後臺線程第4次執行!
後臺線程第5次執行!
後臺線程第6次執行!
後臺線程第7次執行!
Process finished with exit code 0
從上面的執行結果可以看出:
前臺線程是保證執行完畢的,後臺線程還沒有執行完畢就退出了。
實際上:JRE判斷程序是否執行結束的標準是所有的前臺執線程行完畢了,而不管後臺線程的狀態,因此,在使用後臺縣城時候一定要注意這個問題。
實際應用例子:在使用長連接的comet服務端推送技術中,消息推送線程設置爲守護線程,服務於ChatServlet的servlet用戶線程,在servlet的init啓動消息線程,servlet一旦初始化後,一直存在服務器,servlet摧毀後,消息線程自動退出
容器收到一個Servlet請求,調度線程從線程池中選出一個工作者線程,將請求傳遞給該工作者線程,然後由該線程來執行Servlet的 service方法。當這個線程正在執行的時候,容器收到另外一個請求,調度線程同樣從線程池中選出另一個工作者線程來服務新的請求,容器並不關心這個請求是否訪問的是同一個Servlet.當容器同時收到對同一個Servlet的多個請求的時候,那麼這個Servlet的service()方法將在多線程中併發執行。
Servlet容器默認採用單實例多線程的方式來處理請求,這樣減少產生Servlet實例的開銷,提升了對請求的響應時間,對於Tomcat可以在server.xml中通過<Connector>元素設置線程池中線程的數目。
如圖: