設(shè)置一個(gè)時(shí)間,當(dāng)時(shí)間到了的時(shí)候,定時(shí)器就會(huì)去自動(dòng)執(zhí)行某個(gè)邏輯.
timer簡(jiǎn)介
1.創(chuàng)建計(jì)時(shí)器
Timer timer=new Timer();//創(chuàng)建計(jì)時(shí)器
2.創(chuàng)建任務(wù)
timer.schedule(new TimerTask(){//接口,這里不能使用lambada表達(dá)式,lambada表達(dá)式要求接口只有一個(gè)函數(shù),這里有多個(gè)
public void run(){
System.out.println("hello 1000");
}
},1000);//第二個(gè)參數(shù)是推遲時(shí)間,delay,計(jì)時(shí)器創(chuàng)建好之后,什么時(shí)候進(jìn)行執(zhí)行
3.關(guān)掉計(jì)時(shí)器
timer.cancel();//停掉計(jì)時(shí)器,未執(zhí)行的任務(wù)停止
這里timer里面的任務(wù)執(zhí)行完了也并不會(huì)結(jié)束,因?yàn)樗⒉恢朗欠襁€會(huì)添加新的任務(wù)進(jìn)去,
處在嚴(yán)陣以待的狀態(tài)
此時(shí)如果我們想要結(jié)束,應(yīng)該使用cancel主動(dòng)去結(jié)束.
完整代碼:
package thread;
import java.lang.annotation.Target;
import java.util.Timer;
import java.util.TimerTask;
public class ThreadDemo27 {
public static void main(String[] args) throws InterruptedException {
Timer timer=new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello 3000");
}
},3000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello 2000");
}
},2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello 1000");
}
},1000);
System.out.println("hello main");
Thread.sleep(5000);
timer.cancel();
}
}
Timer里面的內(nèi)容
1)需要有一個(gè)線程,負(fù)責(zé)掐時(shí)間,等任務(wù)到達(dá)合適的時(shí)間,這個(gè)線程就負(fù)責(zé)執(zhí)行
還需要有一個(gè)隊(duì)列/數(shù)組,能夠保存所有schedule進(jìn)來(lái)的任務(wù)
直觀的想,就是這個(gè)線程需要不斷的去掃描上述隊(duì)列中的每個(gè)任務(wù)是否到時(shí)間了,到時(shí)間了就需要去執(zhí)行.
在這里每個(gè)任務(wù)都是帶有delay時(shí)間的,肯定是先執(zhí)行時(shí)間小的,后執(zhí)行時(shí)間大的,我們可以使用優(yōu)先級(jí)隊(duì)列,只需要關(guān)注隊(duì)首元素是否到時(shí)間,如果到時(shí)間了,就需要進(jìn)行執(zhí)行,
在此處,我們使用PriortyQueue(線程不安全,需要我們主動(dòng)加鎖控制),
PriorityBlockingQueue(線程安全)在此處場(chǎng)景中,不太好控制,容易出問(wèn)題
自定義計(jì)時(shí)器:
1.首先我們先定義一個(gè)類(lèi),用來(lái)存放時(shí)間和任務(wù).
class MYTIMETASK{
private Runnable runnable;//此處為執(zhí)行的任務(wù)代碼
private long time;//此處為執(zhí)行任務(wù)的時(shí)間,此處的時(shí)間是一個(gè)相對(duì)時(shí)間,此處的時(shí)間是一個(gè)ms級(jí)別的時(shí)間戳
//構(gòu)造方法
//獲取時(shí)間的方法
public long getTime(){
return this.time;
}
//提供任務(wù)執(zhí)行的接口
public void run(){
this.runnable.run();
}
public MYTIMETASK(Runnable runnable,long delay){
this.runnable=runnable;
this.time=System.currentTimeMillis()+delay;
}
}
但是我們?cè)诙x計(jì)時(shí)器MYTIME這個(gè)類(lèi)的時(shí)候,需要用一個(gè)優(yōu)先級(jí)隊(duì)列來(lái)進(jìn)行存儲(chǔ)task任務(wù),因此task必須是可以進(jìn)行比較的,不然會(huì)報(bào)錯(cuò),因此,task這個(gè)類(lèi)就要實(shí)現(xiàn)Comparable這個(gè)接口
這是改進(jìn)后的MTTIMETASK類(lèi)
/**
* 用來(lái)存放執(zhí)行的時(shí)間和任務(wù)
*/
class MYTIMETASK implements Comparable<MYTIMETASK>{
private Runnable runnable;//此處為執(zhí)行的任務(wù)代碼
private long time;//此處為執(zhí)行任務(wù)的時(shí)間,此處的時(shí)間是一個(gè)相對(duì)時(shí)間,此處的時(shí)間是一個(gè)ms級(jí)別的時(shí)間戳
//構(gòu)造方法
//獲取時(shí)間的方法
public long getTime(){
return this.time;
}
//提供任務(wù)執(zhí)行的接口
public void run(){
this.runnable.run();
}
public MYTIMETASK(Runnable runnable,long delay){
this.runnable=runnable;
this.time=System.currentTimeMillis()+delay;
}
@Override
public int compareTo(MYTIMETASK o) {
return (int)(this.time-o.time);//這里是long類(lèi)型,要進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換
}
}
這是MYTIMER類(lèi)的實(shí)現(xiàn)
class MYTIMER{
private Thread t=null;//定義一個(gè)線程來(lái)不斷地進(jìn)行掃描,看是否有任務(wù)到時(shí)間了,不斷地去掃描
private PriorityQueue<MYTIMETASK>queue=new PriorityQueue<>();//此處定義一個(gè)優(yōu)先級(jí)隊(duì)列來(lái)存放任務(wù),
//但是這里需要注意,我們的優(yōu)先級(jí)隊(duì)列在進(jìn)行存儲(chǔ)自定義數(shù)據(jù)時(shí),需要實(shí)現(xiàn)Comparable接口方法.,因此上述代碼應(yīng)做出改動(dòng)
public void schedule(Runnable runnable,long delay){//使用schedule進(jìn)行任務(wù)的添加
MYTIMETASK task=new MYTIMETASK(runnable,delay);
queue.offer(task);//進(jìn)行任務(wù)的添加
}
//在構(gòu)造方法里面創(chuàng)建一個(gè)線程來(lái)進(jìn)行掃描,嚴(yán)陣以待
public MYTIMER(){
t=new Thread(()->{
while(true){
if(queue.isEmpty())
{
continue;//如果隊(duì)列為空,則不能進(jìn)行peek,要循環(huán)到有任務(wù)進(jìn)來(lái)了,才可以執(zhí)行當(dāng)前邏輯
}
//如果不為空,我們則需要查看隊(duì)列頂部的任務(wù)是否到了執(zhí)行的時(shí)間
MYTIMETASK task=queue.peek();
if(System.currentTimeMillis()>=task.getTime()){
task.run();//到了執(zhí)行任務(wù)的時(shí)間,則需要進(jìn)行執(zhí)行
queue.poll();//執(zhí)行了則需要進(jìn)行出隊(duì)列操作
}
}
});
t.start();//開(kāi)啟這個(gè)線程
}
}
但是這里面仍然有不足的地方..
首先這是一個(gè)多線程,我們就要查看是否設(shè)計(jì)線程安全問(wèn)題.
當(dāng)構(gòu)造方法的t線程和主線程在調(diào)用schedule方法時(shí),兩個(gè)線程會(huì)同時(shí)對(duì)queue這個(gè)隊(duì)列進(jìn)行操作.
如上圖而言,這樣就會(huì)出現(xiàn)線程不安全,因此我們要給他們進(jìn)行加鎖.
加上鎖了之后,線程是安全了,但是還有一個(gè)問(wèn)題.
?
當(dāng)隊(duì)列一開(kāi)始為空的時(shí)候,有圖中的線程會(huì)一直執(zhí)行while循環(huán),每次都會(huì)快速的解鎖,但是他每次都會(huì)先一步左邊的線程拿到鎖,因?yàn)樽筮叺逆i是處于沉睡的狀態(tài),才被喚醒會(huì)競(jìng)爭(zhēng)不過(guò)右邊的線程,那么就會(huì)造成一直都是右邊的線程拿到鎖,但是又無(wú)法繼續(xù)執(zhí)行下去.這種情況,我們就應(yīng)該使用wait方法,讓右邊的線程主動(dòng)進(jìn)入WAITING阻塞狀態(tài),主動(dòng)放棄鎖的競(jìng)爭(zhēng),給左邊的線程一點(diǎn)時(shí)間,等到左邊的線程放進(jìn)了任務(wù)的時(shí)候,再去通知右邊線程去執(zhí)行.
相同的道理,如果此時(shí)右側(cè)隊(duì)列的第一個(gè)任務(wù)的時(shí)間過(guò)于長(zhǎng)的話,他就會(huì)一直爭(zhēng)奪鎖,因此我們也要給他設(shè)置一個(gè)帶時(shí)間的wait方法,不然左邊的線程在他一直循環(huán)爭(zhēng)奪鎖的過(guò)程是拿不到鎖的.
最終完整代碼:
/**
* 用來(lái)存放執(zhí)行的時(shí)間和任務(wù)
*/
class MYTIMETASK implements Comparable<MYTIMETASK>{
private Runnable runnable;//此處為執(zhí)行的任務(wù)代碼
private long time;//此處為執(zhí)行任務(wù)的時(shí)間,此處的時(shí)間是一個(gè)相對(duì)時(shí)間,此處的時(shí)間是一個(gè)ms級(jí)別的時(shí)間戳
//構(gòu)造方法
//獲取時(shí)間的方法
public long getTime(){
return this.time;
}
//提供任務(wù)執(zhí)行的接口
public void run(){
this.runnable.run();
}
public MYTIMETASK(Runnable runnable,long delay){
this.runnable=runnable;
this.time=System.currentTimeMillis()+delay;
}
@Override
public int compareTo(MYTIMETASK o) {
return (int)(this.time-o.time);//這里是long類(lèi)型,要進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換
}
}
/**
* 計(jì)時(shí)器,用來(lái)判斷當(dāng)前哪一個(gè)任務(wù)該執(zhí)行了
*/
class MYTIMER{
private Thread t=null;//定義一個(gè)線程來(lái)不斷地進(jìn)行掃描,看是否有任務(wù)到時(shí)間了,不斷地去掃描
private PriorityQueue<MYTIMETASK>queue=new PriorityQueue<>();//此處定義一個(gè)優(yōu)先級(jí)隊(duì)列來(lái)存放任務(wù),
//但是這里需要注意,我們的優(yōu)先級(jí)隊(duì)列在進(jìn)行存儲(chǔ)自定義數(shù)據(jù)時(shí),需要實(shí)現(xiàn)Comparable接口方法.,因此上述代碼應(yīng)做出改動(dòng)
private Object locker1=new Object();
public void schedule(Runnable runnable,long delay){//使用schedule進(jìn)行任務(wù)的添加
synchronized (locker1) {
MYTIMETASK task = new MYTIMETASK(runnable, delay);
queue.offer(task);//進(jìn)行任務(wù)的添加
locker1.notify();
}
}
//提供一個(gè)cancel方法來(lái)進(jìn)行計(jì)時(shí)器任務(wù)的結(jié)束
public void cancel(){
t.interrupt();
}
//在構(gòu)造方法里面創(chuàng)建一個(gè)線程來(lái)進(jìn)行掃描,嚴(yán)陣以待
public MYTIMER(){
t=new Thread(()->{
while(!Thread.currentThread().isInterrupted()){
try {
synchronized (locker1) {
while (queue.isEmpty()) {
locker1.wait();
//continue;//如果隊(duì)列為空,則不能進(jìn)行peek,要循環(huán)到有任務(wù)進(jìn)來(lái)了,才可以執(zhí)行當(dāng)前邏輯
}
//如果不為空,我們則需要查看隊(duì)列頂部的任務(wù)是否到了執(zhí)行的時(shí)間
MYTIMETASK task = queue.peek();
if (System.currentTimeMillis() >= task.getTime()) {
task.run();//到了執(zhí)行任務(wù)的時(shí)間,則需要進(jìn)行執(zhí)行
queue.poll();//執(zhí)行了則需要進(jìn)行出隊(duì)列操作
}
else{
locker1.wait(task.getTime()-System.currentTimeMillis());
//此處的wait有兩種方式,一種是當(dāng)沒(méi)有任務(wù)添加進(jìn)來(lái)的時(shí)候,這個(gè)線程會(huì)一直等到//時(shí)間到了繼續(xù)再去執(zhí)行,
//另外一種是又有新添加的任務(wù),這個(gè)線程要掃描當(dāng)前任務(wù)時(shí)間最近的線程,這個(gè)新添加的任務(wù)就有可能是最近時(shí)間最接近的任務(wù)
}
}
}
catch (InterruptedException e){
break;
}
}
});
t.start();//開(kāi)啟這個(gè)線程
}
}
測(cè)試代碼:
public class Main{
public static void main(String[] args) throws InterruptedException {
MYTIMER timer=new MYTIMER();
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello 3000");
}
},3000);
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello 2000");
}
},2000);
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello 1000");
}
},1000);
Thread.sleep(5000);
timer.cancel();
}
}
運(yùn)行結(jié)果:?
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-755427.html
以上就是計(jì)時(shí)器的自我實(shí)現(xiàn),感謝各位大佬的三連.文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-755427.html
到了這里,關(guān)于JAVAEE-定時(shí)器案例的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!