守护线程总结
2019獨角獸企業重金招聘Python工程師標準>>>
在Java中有兩類線程:User Thread(用戶線程)、Daemon Thread(守護線程)?
??? 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離開了,虛擬機也就退出運行了。?
Java代碼??
//完成文件輸出的守護線程任務??
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 :?
Java代碼??
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();??
}??
}??
}??
}??
Java代碼??
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>元素設置線程池中線程的數目。?
如圖:?
?
為什么要用守護線程,見Web應用程序中調度器的啟動和關閉問題
轉載于:https://my.oschina.net/u/1472917/blog/396882
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
- 上一篇: 《架构师(“拥抱2015”特刊)》发布
- 下一篇: TCP/IP入门(4) --应用层