Java并發(fā)編程示例(七):守護(hù)線程的創(chuàng)建和運(yùn)行
Java有一種特殊線程,守護(hù)線程,這種線程優(yōu)先級(jí)特別低,只有在同一程序中的其他線程不執(zhí)行時(shí)才會(huì)執(zhí)行。
由于守護(hù)線程擁有這些特性,所以,一般用為為程序中的普通線程(也稱為用戶線程)提供服務(wù)。它們一般會(huì)有一個(gè)無(wú)限循環(huán),或用于等待請(qǐng)求服務(wù),或用于執(zhí)行任務(wù)等。它們不可以做任何重要的工作,因?yàn)槲覀儾淮_定他們什么時(shí)才能分配到CPU運(yùn)行時(shí)間,而且當(dāng)沒有其他線程執(zhí)行時(shí),它們就會(huì)自動(dòng)終止。這類線程的一個(gè)典型應(yīng)用就是Java的垃圾回收。
在本節(jié)示例中,我們將創(chuàng)建兩個(gè)線程,一個(gè)是普通線程,向隊(duì)列中寫入事件;另外一個(gè)是守護(hù)線程,清除隊(duì)列中的事件,刪除存在時(shí)間超過10秒的事件。
知其然
按照如下步驟,實(shí)現(xiàn)示例程序。
1.創(chuàng)建Event類,該類僅僅用于保存程序執(zhí)行所需的事件信息。聲明兩個(gè)屬性,一個(gè)是java.util.Date類型的的date熟悉,另外一個(gè)是String類型的event屬性;然后生成這兩個(gè)屬性的讀寫方法。代碼如下:
public class Event {
private Date date;
private String event;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getEvent() {
return event;
}
public void setEvent(String event) {
this.event = event;
}
}
2.創(chuàng)建一個(gè)名為WriterTask的類,并且實(shí)現(xiàn)Runnable接口。代碼如下:
public class WriterTask implements Runnable {
3.聲明一個(gè)用來(lái)存儲(chǔ)事件的隊(duì)列屬性,實(shí)現(xiàn)類的構(gòu)造函數(shù),并且利用其參數(shù)來(lái)初始化隊(duì)列屬性。代碼如下:
private Deque<Event> deque;
public WriterTask(Deque<Event> deque) {
this.deque = deque;
}
4.實(shí)現(xiàn)該任務(wù)的run()方法,方法中含有一個(gè)遍歷100次的循環(huán)。在每次遍歷中,創(chuàng)建一個(gè)新的Event對(duì)象,然后保存到隊(duì)列中,再睡眠1秒鐘。代碼如下:
@Override
public void run() {
for (int i = 0; i < 100; i++) {
Event event = new Event();
event.setDate(new Date());
event.setEvent(String.format("The thread %s has generated an event",
Thread.currentThread().getId()));
deque.addFirst(event);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
5.創(chuàng)建一個(gè)名為CleanerTask的類,并繼承Thread類。代碼如下:
public class CleanerTask extends Thread {
6.聲明一個(gè)用來(lái)存儲(chǔ)事件的隊(duì)列屬性,實(shí)現(xiàn)類的構(gòu)造函數(shù),并且利用其參數(shù)來(lái)初始化隊(duì)列屬性。在構(gòu)造方法中,通過調(diào)用setDaemon()方法,將該線程設(shè)置為守護(hù)線程。代碼如下:
private Deque<Event> deque;
public CleanerTask(Deque<Event> deque) {
this.deque = deque;
setDaemon(true);
}
7.實(shí)現(xiàn)run()方法,方法體內(nèi)有一個(gè)無(wú)限循環(huán),用于獲取當(dāng)前時(shí)間,然后調(diào)用clearn()方法。代碼如下:
@Override
public void run() {
while (true) {
Date date = new Date();
clean(date);
}
}
8.實(shí)現(xiàn)clean()方法,在該方法內(nèi),獲取最后面的一個(gè)時(shí)間,然后檢查時(shí)間時(shí)間和當(dāng)前時(shí)間的時(shí)間差,如果在10秒鐘之前創(chuàng)建的,則刪除當(dāng)前事件,再檢查下一個(gè)事件。如果有事件被刪除,則顯示打印出被刪除事件的信息,然后還將打印出隊(duì)列的最新長(zhǎng)度,這樣就可以觀察到程序的執(zhí)行進(jìn)展。代碼如下:
private void clean(Date date) {
long difference;
boolean delete;
if (deque.size() == 0) {
return;
}
delete = false;
do {
Event e = deque.getLast();
difference = date.getTime() - e.getDate().getTime();
if (difference > 10000) {
System.out.printf("Cleaner: %s\n", e.getDate());
deque.removeLast();
delete = true;
}
} while (difference > 10000);
if (delete) {
System.out.printf("Clearner: Size of the queue: %d\n", deque.size());
}
}
9.創(chuàng)建程序的主類,Main類,然后實(shí)現(xiàn)main()方法。代碼如下:
public class Main {
public static void main(String[] args) {
10.使用Deque類創(chuàng)建存儲(chǔ)事件的隊(duì)列。代碼如下:
Deque<Event> deque = new ArrayDeque<>();
11.創(chuàng)建并啟動(dòng)三個(gè)WriterTask線程和一個(gè)CleanerTask線程。代碼如下:
Deque<Event> deque = new ArrayDeque<>();
WriterTask writer = new WriterTask(deque);
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(writer);
thread.start();
}
CleanerTask cleaner = new CleanerTask(deque);
cleaner.start();
12.執(zhí)行程序,查看執(zhí)行結(jié)果。
知其所以然
分析程序的執(zhí)行結(jié)果就可以看出,隊(duì)列先增加到30,然后就在27到30之間變化,知道程序執(zhí)行結(jié)束。
程序首先從三個(gè)WriterTask線程開始執(zhí)行,每個(gè)線程先隊(duì)列增加一個(gè)事件,然后睡眠1秒鐘。在前10秒過后,在隊(duì)列中將存在三十個(gè)事件。在這10秒期間,當(dāng)三個(gè)WriterTask線程都睡眠時(shí),CleanerTask線程也會(huì)運(yùn)行,但是不會(huì)刪除任何事件,因?yàn)樗械氖录纳蓵r(shí)間還不超過10秒。在前10秒過后的時(shí)間里,每秒鐘三個(gè)WriterTask向隊(duì)列中添加三個(gè)事件;同樣,CleanerTask每秒會(huì)刪除三個(gè)事件。所以,事件的數(shù)目在27到30之間徘徊。
當(dāng)WriterTask線程都休眠時(shí),我們就可以自由處理時(shí)間,這段時(shí)間讓守護(hù)線程得以運(yùn)行。如果將WriterTask線程的睡眠時(shí)間設(shè)置得更短一點(diǎn),那么CleanerTask線程將獲取更少的CPU運(yùn)行時(shí)間。果真如此的話,因?yàn)镃leanerTask線程一直得不到足夠的運(yùn)行時(shí)間不能用于刪除足夠的事件,隊(duì)列的長(zhǎng)度將會(huì)一直增長(zhǎng)下去。
永無(wú)止境
僅能在調(diào)用start()方法之前,通過調(diào)用setDaemon()方法將線程設(shè)置為守護(hù)線程。一旦線程開始運(yùn)行,則不能修改守護(hù)狀態(tài)。
還可以使用isDaemon()來(lái)檢查一個(gè)線程是否為守護(hù)線程。如果是守護(hù)線程,則返回true;如果是普通線程,則返回false。
拿來(lái)主義
本文是從 《Java 7 Concurrency Cookbook》 (D瓜哥竊譯為 《Java7并發(fā)示例集》 )翻譯而來(lái),僅作為學(xué)習(xí)資料使用。沒有授權(quán),不得用于任何商業(yè)行為。
小有所成
本節(jié)所用的所有示例代碼的完整版。
Event類的完整代碼
package com.diguage.books.concurrencycookbook.chapter1.recipe7;
import java.util.Date;
/**
* 事件信息類
* Date: 2013-09-19
* Time: 22:56
*/
public class Event {
private Date date;
private String event;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getEvent() {
return event;
}
public void setEvent(String event) {
this.event = event;
}
}
WriterTask類的完整代碼
package com.diguage.books.concurrencycookbook.chapter1.recipe7;
import java.util.Date;
import java.util.Deque;
import java.util.concurrent.TimeUnit;
/**
* 每秒生成一個(gè)事件。
* Date: 2013-09-19
* Time: 22:59
*/
public class WriterTask implements Runnable {
private Deque<Event> deque;
public WriterTask(Deque<Event> deque) {
this.deque = deque;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
Event event = new Event();
event.setDate(new Date());
event.setEvent(String.format("The thread %s has generated an event",
Thread.currentThread().getId()));
deque.addFirst(event);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
CleanerTask類的完整代碼
package com.diguage.books.concurrencycookbook.chapter1.recipe7;
import java.util.Date;
import java.util.Deque;
/**
* 事件清理
* Date: 2013-09-19
* Time: 23:33
*/
public class CleanerTask extends Thread {
private Deque<Event> deque;
public CleanerTask(Deque<Event> deque) {
this.deque = deque;
setDaemon(true);
}
@Override
public void run() {
while (true) {
Date date = new Date();
clean(date);
}
}
/**
* 刪除事件。
*
* @param date
*/
private void clean(Date date) {
long difference;
boolean delete;
if (deque.size() == 0) {
return;
}
delete = false;
do {
Event e = deque.getLast();
difference = date.getTime() - e.getDate().getTime();
if (difference > 10000) {
System.out.printf("Cleaner: %s\n", e.getDate());
deque.removeLast();
delete = true;
}
} while (difference > 10000);
if (delete) {
System.out.printf("Clearner: Size of the queue: %d\n", deque.size());
}
}
}
Main類的完整代碼
package com.diguage.books.concurrencycookbook.chapter1.recipe7;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* Date: 2013-09-19
* Time: 23:54
*/
public class Main {
public static void main(String[] args) {
Deque<Event> deque = new ArrayDeque<>();
WriterTask writer = new WriterTask(deque);
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(writer);
thread.start();
}
CleanerTask cleaner = new CleanerTask(deque);
cleaner.start();
}
}
相關(guān)文章
Mybatis generator如何自動(dòng)生成代碼
這篇文章主要介紹了Mybatis generator如何自動(dòng)生成代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12Intellij IDEA中啟動(dòng)多個(gè)微服務(wù)(開啟Run Dashboard管理)
這篇文章主要介紹了Intellij IDEA中啟動(dòng)多個(gè)微服務(wù)(開啟Run Dashboard管理),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07SpringBoot http post請(qǐng)求數(shù)據(jù)大小設(shè)置操作
這篇文章主要介紹了SpringBoot http post請(qǐng)求數(shù)據(jù)大小設(shè)置操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2020-09-09Spring 使用 feign時(shí)設(shè)置header信息的操作
這篇文章主要介紹了Spring 使用 feign時(shí)設(shè)置header信息的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08Junit 5中@ParameterizedTest與@EnumSource結(jié)合使用
今天小編就為大家分享一篇關(guān)于Junit 5中@ParameterizedTest與@EnumSource結(jié)合使用,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12elasticsearch啟動(dòng)警告無(wú)法鎖定JVM內(nèi)存
今天小編就為大家分享一篇關(guān)于elasticsearch啟動(dòng)警告無(wú)法鎖定JVM內(nèi)存,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03Spring Security之默認(rèn)的過濾器鏈及自定義Filter操作
這篇文章主要介紹了Spring Security之默認(rèn)的過濾器鏈及自定義Filter操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06