專欄:JavaEE初階起飛計劃
個人主頁:手握風云
目錄
一、多線程案例
1.1. 定時器
一、多線程案例
1.1. 定時器
? ? ? ? 定時器是軟件開發的一個重要組件,是一種能夠按照預設的時間間隔或在特定時間點執行某個任務或代碼片段的機制。你可以把它想象成一個鬧鐘,只不過這個“鬧鐘”不是提醒你去起床,而是提醒計算機去執行某個特定的操作。定時器與阻塞隊列一致,也會被單獨封裝成一個或一組服務器來使用。
- 標準庫中的定時器
import java.util.Timer;
import java.util.TimerTask;public class Demo1 {public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("定時器執行任務 3000");}}, 3000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("定時器執行任務 2000");}}, 2000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("定時器執行任務 1000");}}, 1000);System.out.println("程序啟動");}
}
- 定時器的實現
? ? ? ? 對于自主實現的定時器,需要指定等待的最大時間,如果等不到,還需要執行其他的操作。
class MyTimer {public MyTimer() {}//向定時器中添加任務public void schedule(Runnable runnable, long delay) {}
}
? ? ? ? 這里注意,TimerTask類實現的是Runnable接口,所以我們在schedule方法里面添加的也是Runnable類型的參數。既然要對任務進行組織管理,就得使用合適的數據結構,比如順序表、棧。但是這些任務不一定是按照時間順序添加的,并且添加的順序和執行順序沒太大關系。如果使用順序表,執行任務時,就需要遍歷來找到時間最小的任務,效率太低。這時我們就可以使用堆來解決。
? ? ? ? 對于堆所存放的泛型參數,這里不能添加成Runnable,因為堆里面的任務不只是內容,還需要考慮任務的時間。
class MyTimerTask {private Runnable task;// 這個地方為了和當前時間對比,確認任務是否執行,需要保存絕對的時間戳private long time;public MyTimerTask(Runnable task, long delay) {this.task = task;this.time = System.currentTimeMillis() + delay;}public Runnable getTask() {return task;}public long getTime() {return time;}
}class MyTimer {private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();public MyTimer() {}//向定時器中添加任務public void schedule(Runnable runnable, long delay) {queue.offer(new MyTimerTask(runnable, delay));}
}
? ? ? ? 由于MyTimerTask是放在優先級隊列中,所以我們還需要寫出比較規則。
class MyTimerTask implements Comparable<MyTimerTask>{private Runnable task;// 這個地方為了和當前時間對比,確認任務是否執行,需要保存絕對的時間戳private long time;public MyTimerTask(Runnable task, long delay) {this.task = task;this.time = System.currentTimeMillis() + delay;}public Runnable getTask() {return task;}public long getTime() {return time;}@Overridepublic int compareTo(MyTimerTask o) {return (int) (this.time - o.time);}
}
? ? ? ? 接下來就是創建線程,讓線程來檢測任務是否到時間了,以及去執行這個任務。我們需要循環從隊列中取出元素,判斷是否到時間了,如果到達就出隊列,沒有就不做處理。
import java.util.PriorityQueue;class MyTimerTask implements Comparable<MyTimerTask>{private Runnable task;// 這個地方為了和當前時間對比,確認任務是否執行,需要保存絕對的時間戳private long time;public MyTimerTask(Runnable task, long delay) {this.task = task;this.time = System.currentTimeMillis() + delay;}public Runnable getTask() {return task;}public long getTime() {return time;}@Overridepublic int compareTo(MyTimerTask o) {return (int) (this.time - o.time);}
}class MyTimer {private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();public MyTimer() {Thread t = new Thread(() -> {while (true) {if (queue.isEmpty()) {continue;}MyTimerTask task = queue.peek();long curTime = System.currentTimeMillis();if (curTime < task.getTime()) {continue;} else {task.getTask().run();queue.poll();}}});t.start();}//向定時器中添加任務public void schedule(Runnable runnable, long delay) {queue.offer(new MyTimerTask(runnable, delay));}
}public class Demo2 {public static void main(String[] args) {MyTimer timer = new MyTimer();timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("定時任務:3000");}}, 3000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("定時任務:2000");}}, 2000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("定時任務:1000");}}, 1000);}
}
? ? ? ? 雖然執行效果沒有什么問題,但上面的操作對于同一個隊列進行出入,所以線程是不安全的。那我們就需要在入隊列和出隊列的操作里面都要進行加鎖。第二個問題,就是忙等出現餓死。如上面的代碼,當隊列為空或者沒到時間時,不做任何處理,只消耗CPU資源,沒有任何實質性的進展。尤其是第二個continue,這里就相當于30分鐘之后要去執行某項任務,每隔1分鐘就得看一下時間,當我們設計了等待時間之后,到時間自動喚醒或者有優先級更高的任務要去執行。
? ? ? ? 完整代碼:
import java.util.PriorityQueue;class MyTimerTask implements Comparable<MyTimerTask>{private Runnable task;// 這個地方為了和當前時間對比,確認任務是否執行,需要保存絕對的時間戳private long time;public MyTimerTask(Runnable task, long delay) {this.task = task;this.time = System.currentTimeMillis() + delay;}public Runnable getTask() {return task;}public long getTime() {return time;}@Overridepublic int compareTo(MyTimerTask o) {return (int) (this.time - o.time);}
}class MyTimer {private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();private Object locker = new Object();public MyTimer() {Thread t = new Thread(() -> {while (true) {try {synchronized (locker) {if (queue.isEmpty()) {locker.wait();}MyTimerTask task = queue.peek();long curTime = System.currentTimeMillis();if (curTime < task.getTime()) {locker.wait(task.getTime() - curTime);} else {task.getTask().run();queue.poll();}}} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}//向定時器中添加任務public void schedule(Runnable runnable, long delay) {synchronized (locker) {queue.offer(new MyTimerTask(runnable, delay));locker.notify();}}
}public class Demo2 {public static void main(String[] args) {MyTimer timer = new MyTimer();timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("定時任務:3000");}}, 3000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("定時任務:2000");}}, 2000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("定時任務:1000");}}, 1000);}
}