守护线程总结
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
在Java中有兩類線程:User Thread(用戶線程)、Daemon Thread(守護(hù)線程)?
??? Daemon的作用是為其他線程的運(yùn)行提供便利服務(wù),比如垃圾回收線程就是一個(gè)很稱職的守護(hù)者。User和Daemon兩者幾乎沒(méi)有區(qū)別,唯一的不同之處就在于虛擬機(jī)的離開(kāi):如果 User Thread已經(jīng)全部退出運(yùn)行了,只剩下Daemon Thread存在了,虛擬機(jī)也就退出了。 因?yàn)闆](méi)有了被守護(hù)者,Daemon也就沒(méi)有工作可做了,也就沒(méi)有繼續(xù)運(yùn)行程序的必要了。?
??? 值得一提的是,守護(hù)線程并非只有虛擬機(jī)內(nèi)部提供,用戶在編寫程序時(shí)也可以自己設(shè)置守護(hù)線程。下面的方法就是用來(lái)設(shè)置守護(hù)線程的。?
??? public final void setDaemon(boolean on)?
??? 這里有幾點(diǎn)需要注意:?
??? (1) thread.setDaemon(true)必須在thread.start()之前設(shè)置,否則會(huì)跑出一個(gè)IllegalThreadStateException異常。你不能把正在運(yùn)行的常規(guī)線程設(shè)置為守護(hù)線程。??
??? (2) 在Daemon線程中產(chǎn)生的新線程也是Daemon的。??
??? (3) 不要認(rèn)為所有的應(yīng)用都可以分配給Daemon來(lái)進(jìn)行服務(wù),比如讀寫操作或者計(jì)算邏輯。?
?????? 因?yàn)槟悴豢赡苤涝谒械腢ser完成之前,Daemon是否已經(jīng)完成了預(yù)期的服務(wù)任務(wù)。一旦User退出了,可能大量數(shù)據(jù)還沒(méi)有來(lái)得及讀入或?qū)懗?#xff0c;計(jì)算任務(wù)也可能多次運(yùn)行結(jié)果不一樣。這對(duì)程序是毀滅性的。造成這個(gè)結(jié)果理由已經(jīng)說(shuō)過(guò)了:一旦所有User Thread離開(kāi)了,虛擬機(jī)也就退出運(yùn)行了。?
Java代碼??
//完成文件輸出的守護(hù)線程任務(wù)??
import?java.io.*;?????
????
class?TestRunnable?implements?Runnable{?????
????public?void?run(){?????
???????????????try{?????
??????????????????Thread.sleep(1000);//守護(hù)線程阻塞1秒后運(yùn)行?????
??????????????????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);?//設(shè)置守護(hù)線程?????
????????thread.start();?//開(kāi)始執(zhí)行分進(jìn)程?????
????}?????
}?????
//運(yùn)行結(jié)果:文件daemon.txt中沒(méi)有"daemon"字符串。??
看到了吧,把輸入輸出邏輯包裝進(jìn)守護(hù)線程多么的可怕,字符串并沒(méi)有寫入指定文件。原因也很簡(jiǎn)單,直到主線程完成,守護(hù)線程仍處于1秒的阻塞狀態(tài)。這個(gè)時(shí)候主線程很快就運(yùn)行完了,虛擬機(jī)退出,Daemon停止服務(wù),輸出操作自然失敗了。?
例子2 :?
Java代碼??
public?class?Test?{??
public?static?void?main(String?args)?{??
Thread?t1?=?new?MyCommon();??
Thread?t2?=?new?Thread(new?MyDaemon());??
t2.setDaemon(true);?//設(shè)置為守護(hù)線程??
t2.start();??
t1.start();??
}??
}??
class?MyCommon?extends?Thread?{??
public?void?run()?{??
for?(int?i?=?0;?i?<?5;?i++)?{??
System.out.println("線程1第"?+?i?+?"次執(zhí)行!");??
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("后臺(tái)線程第"?+?i?+?"次執(zhí)行!");??
try?{??
Thread.sleep(7);??
}?catch?(InterruptedException?e)?{??
e.printStackTrace();??
}??
}??
}??
}??
后臺(tái)線程第0次執(zhí)行!?
線程1第0次執(zhí)行!?
線程1第1次執(zhí)行!?
后臺(tái)線程第1次執(zhí)行!?
后臺(tái)線程第2次執(zhí)行!?
線程1第2次執(zhí)行!?
線程1第3次執(zhí)行!?
后臺(tái)線程第3次執(zhí)行!?
線程1第4次執(zhí)行!?
后臺(tái)線程第4次執(zhí)行!?
后臺(tái)線程第5次執(zhí)行!?
后臺(tái)線程第6次執(zhí)行!?
后臺(tái)線程第7次執(zhí)行!?
Process finished with exit code 0?
從上面的執(zhí)行結(jié)果可以看出:?
前臺(tái)線程是保證執(zhí)行完畢的,后臺(tái)線程還沒(méi)有執(zhí)行完畢就退出了。?
實(shí)際上:JRE判斷程序是否執(zhí)行結(jié)束的標(biāo)準(zhǔn)是所有的前臺(tái)執(zhí)線程行完畢了,而不管后臺(tái)線程的狀態(tài),因此,在使用后臺(tái)縣城時(shí)候一定要注意這個(gè)問(wèn)題。?
實(shí)際應(yīng)用例子:在使用長(zhǎng)連接的comet服務(wù)端推送技術(shù)中,消息推送線程設(shè)置為守護(hù)線程,服務(wù)于ChatServlet的servlet用戶線程,在servlet的init啟動(dòng)消息線程,servlet一旦初始化后,一直存在服務(wù)器,servlet摧毀后,消息線程自動(dòng)退出?
容器收到一個(gè)Servlet請(qǐng)求,調(diào)度線程從線程池中選出一個(gè)工作者線程,將請(qǐng)求傳遞給該工作者線程,然后由該線程來(lái)執(zhí)行Servlet的 service方法。當(dāng)這個(gè)線程正在執(zhí)行的時(shí)候,容器收到另外一個(gè)請(qǐng)求,調(diào)度線程同樣從線程池中選出另一個(gè)工作者線程來(lái)服務(wù)新的請(qǐng)求,容器并不關(guān)心這個(gè)請(qǐng)求是否訪問(wèn)的是同一個(gè)Servlet.當(dāng)容器同時(shí)收到對(duì)同一個(gè)Servlet的多個(gè)請(qǐng)求的時(shí)候,那么這個(gè)Servlet的service()方法將在多線程中并發(fā)執(zhí)行。?
??????????? Servlet容器默認(rèn)采用單實(shí)例多線程的方式來(lái)處理請(qǐng)求,這樣減少產(chǎn)生Servlet實(shí)例的開(kāi)銷,提升了對(duì)請(qǐng)求的響應(yīng)時(shí)間,對(duì)于Tomcat可以在server.xml中通過(guò)<Connector>元素設(shè)置線程池中線程的數(shù)目。?
如圖:?
?
為什么要用守護(hù)線程,見(jiàn)Web應(yīng)用程序中調(diào)度器的啟動(dòng)和關(guān)閉問(wèn)題
轉(zhuǎn)載于:https://my.oschina.net/u/1472917/blog/396882
與50位技術(shù)專家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖總結(jié)
- 上一篇: 《架构师(“拥抱2015”特刊)》发布
- 下一篇: TCP/IP入门(4) --应用层