什么是定時(shí)器?
定時(shí)器的功能和“鬧鐘”類似,代碼中的定時(shí)器通常都是“多長(zhǎng)時(shí)間之后,執(zhí)行某個(gè)動(dòng)作”。
定時(shí)器的實(shí)現(xiàn)
標(biāo)準(zhǔn)庫(kù)中的定時(shí)器
// 標(biāo)準(zhǔn)庫(kù)的定時(shí)器 執(zhí)行完任務(wù)之后線程不會(huì)退出 需要手動(dòng)殺死線程
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("執(zhí)行第一次定時(shí)器任務(wù)");
}
},3000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("執(zhí)行第二次定時(shí)器任務(wù)");
}
},4000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("執(zhí)行第三次定時(shí)器任務(wù)");
}
},5000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("執(zhí)行第四次定時(shí)器任務(wù)");
}
},6000);
System.out.println("定時(shí)器啟動(dòng)");
}
注:
- 一個(gè)定時(shí)器可以安排多個(gè)任務(wù)。
- 調(diào)用schedule安排任務(wù)時(shí),一定要重寫run方法,明確任務(wù)內(nèi)容
- 定時(shí)器開(kāi)啟后不會(huì)自動(dòng)結(jié)束,得手動(dòng)殺死進(jìn)程
自己實(shí)現(xiàn)一個(gè)定時(shí)器
版本一:實(shí)現(xiàn)簡(jiǎn)單的定時(shí)器
- schedule方法是用來(lái)描述任務(wù)內(nèi)容的。第一個(gè)參數(shù)是:任務(wù)內(nèi)容;第二個(gè)參數(shù)是:任務(wù)多長(zhǎng)時(shí)間后執(zhí)行
public MyTask(Runnable runnable, long delay){
this.runnable = runnable;
this.time = System.currentTimeMillis() + delay;
}
public void schedule(Runnable runnable,long delay){
MyTask myTask = new MyTask(runnable, delay);
}
-
用什么數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)任務(wù)呢?
我們首先要考慮的是要讓任務(wù)設(shè)置時(shí)間最短的先執(zhí)行,這就有個(gè)優(yōu)先級(jí)的問(wèn)題,所以使用優(yōu)先級(jí)隊(duì)列
同時(shí),我們通常都會(huì)使用多個(gè)線程來(lái)執(zhí)行這些設(shè)置的任務(wù),就要保證線程安全。
綜上,我們最后決定使用優(yōu)先級(jí)阻塞隊(duì)列來(lái)存儲(chǔ)任務(wù)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-715521.html
-
我們創(chuàng)建一個(gè)專門的線程來(lái)執(zhí)行定時(shí)器里的任務(wù)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-715521.html
//最基礎(chǔ)的定時(shí)器完成
class MyTimer{
private PriorityBlockingQueue<MyTask> priorityBlockingQueue = new PriorityBlockingQueue<>();
public MyTimer(){
Thread thread = new Thread(() -> {
while (true){
try {
MyTask myTask = priorityBlockingQueue.take();
if (System.currentTimeMillis() >= myTask.getTime()){
myTask.getRunnable().run();
}else {
priorityBlockingQueue.put(myTask);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
private void schedule(Runnable runnable,long delay){
MyTask myTask = new MyTask(runnable,delay);
priorityBlockingQueue.put(myTask);
}
}
版本二:解決thread線程的忙等問(wèn)題
- 當(dāng)定時(shí)器啟動(dòng)后有一個(gè)問(wèn)題:不管任務(wù)時(shí)間有多久,thread線程一直執(zhí)行,會(huì)占用CPU資源。我們想個(gè)辦法讓它在任務(wù)時(shí)間還沒(méi)到的時(shí)候停下來(lái),就要使用wait-notify
class MyTimer{
private PriorityBlockingQueue<MyTask> priorityBlockingQueue = new PriorityBlockingQueue<>();
private Object locker = new Object();
public MyTimer(){
Thread thread = new Thread(() -> {
while (true){
try {
MyTask myTask = priorityBlockingQueue.take();
if (System.currentTimeMillis() >= myTask.getTime()){
myTask.getRunnable().run();
}else {
priorityBlockingQueue.put(myTask);
synchronized (locker){
locker.wait(myTask.getTime() - System.currentTimeMillis());
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
private void schedule(Runnable runnable,long delay){
MyTask myTask = new MyTask(runnable,delay);
priorityBlockingQueue.put(myTask);
}
}
版本三:解決漏掉任務(wù)的問(wèn)題
- 又一個(gè)問(wèn)題:如果schedule線程中新添加了一個(gè)任務(wù) 執(zhí)行時(shí)間比wait時(shí)間短 不就錯(cuò)過(guò)了嘛。使用notify
class MyTimer{
private PriorityBlockingQueue<MyTask> priorityBlockingQueue = new PriorityBlockingQueue<>();
private Object locker = new Object();
public MyTimer(){
Thread thread = new Thread(() -> {
while (true){
try {
MyTask myTask = priorityBlockingQueue.take();
if (System.currentTimeMillis() >= myTask.getTime()){
myTask.getRunnable().run();
}else {
priorityBlockingQueue.put(myTask);
synchronized (locker){
locker.wait(myTask.getTime() - System.currentTimeMillis());
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
private void schedule(Runnable runnable,long delay){
MyTask myTask = new MyTask(runnable,delay);
priorityBlockingQueue.put(myTask);
synchronized (locker){
locker.notify();
}
}
}
版本四:解決notify先執(zhí)行的問(wèn)題
- 如果notify先執(zhí)行了 take在notify之前執(zhí)行 也就是說(shuō)放進(jìn)去了新的元素 但是take這邊的線程沒(méi)感覺(jué)到不就還沒(méi)起到效果嘛。 擴(kuò)大鎖的范圍
class MyTimer{
private PriorityBlockingQueue<MyTask> priorityBlockingQueue = new PriorityBlockingQueue<>();
private Object locker = new Object();
public MyTimer(){
Thread thread = new Thread(() -> {
while (true){
try {
synchronized (locker) {
MyTask myTask = priorityBlockingQueue.take();
if (System.currentTimeMillis() >= myTask.getTime()) {
myTask.getRunnable().run();
} else {
priorityBlockingQueue.put(myTask);
locker.wait(myTask.getTime() - System.currentTimeMillis());
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
public void schedule(Runnable runnable,long delay){
MyTask myTask = new MyTask(runnable, delay);
priorityBlockingQueue.put(myTask);
//這里起到的作用就是 刷新一下 看看隊(duì)列中有沒(méi)有執(zhí)行時(shí)間更早的任務(wù) 有兩種情況
//1. 先放進(jìn)去再刷新查看 先放進(jìn)去最開(kāi)始沒(méi)注意到也沒(méi)事兒 刷新的時(shí)候就會(huì)注意到重新計(jì)算wait時(shí)間
//2. 先查看再放進(jìn)去 會(huì)死鎖(把notify的鎖范圍放大) 在一開(kāi)始隊(duì)列里沒(méi)有數(shù)據(jù)的時(shí)候 會(huì)等待添加元素 但添加元素得獲取到鎖 此時(shí)阻塞的時(shí)候已經(jīng)鎖上了take
// 沒(méi)有人釋放鎖 所以 死鎖了
synchronized (locker) {
locker.notify();
}
}
}
到了這里,關(guān)于多線程---定時(shí)器的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!