<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試必問!JDK 中定時器是如何實現(xiàn)的?

          共 21056字,需瀏覽 43分鐘

           ·

          2021-04-01 13:08


          jdk中能夠?qū)崿F(xiàn)定時器功能的大致有三種方式:

          • java.util.Timer

          • java.util.concurrent.DelayQueue

          • java.util.concurrent.ScheduledThreadPoolExecutor

          靜下心來,咱們一一探究。

          一. java.util.Timer

          示例代碼:

          /**
           * 安排指定的任務(wù)task在指定的時間firstTime開始進行重復(fù)的固定速率period執(zhí)行
           * 每天中午12點都執(zhí)行一次
           *
           * @author Fooisart
           * Created on 21:46 14-01-2019
           */
          public class TimerDemo {
              public static void main(String[] args) {
                  Timer timer = new Timer();
                  Calendar calendar = Calendar.getInstance();
                  calendar.set(Calendar.HOUR_OF_DAY, 12);//控制小時
                  calendar.set(Calendar.MINUTE, 0);//控制分鐘
                  calendar.set(Calendar.SECOND, 0);//控制秒
                  Date time = calendar.getTime();//執(zhí)行任務(wù)時間為12:00:00

                  //每天定時12:00執(zhí)行操作,每隔2秒執(zhí)行一次
                  timer.schedule(new TimerTask() {
                      @Override
                      public void run() {
                          System.out.println(new Date() + "執(zhí)行任務(wù)。。。");
                      }
                  }, time, 1000 * 2);
              }
          }

          Demo中使用了Timer實現(xiàn)了一個定時任務(wù),該任務(wù)在每天12點開始執(zhí)行,并且每隔2秒執(zhí)行一次。

          順手牽羊:查看源碼時,無意發(fā)現(xiàn)Timer中有schedule與scheduleAtFixedRate,它倆都可以到約定時間按照指定時間間隔執(zhí)行。然而它倆的區(qū)別是什么呢?官方解釋:一個是Fixed-delay,一個是Fixed-rate。那么這兩個詞到底是什么意思呢?把demo中的代碼運行一遍,然后把schedule換成scheduleAtFixedRate,就全部了然了。

          示例代碼中較為簡潔,能看出控制執(zhí)行時間的方法應(yīng)該是 timer.schedule(),跟進去看源碼:

          public void schedule(TimerTask task, Date firstTime, long period) {        if (period <= 0)            throw new IllegalArgumentException("Non-positive period.");        sched(task, firstTime.getTime(), -period);    }
          • task 表示要執(zhí)行的任務(wù)邏輯

          • firstTime 表示第一次執(zhí)行的時間

          • period 表示每次間隔時間

          繼續(xù)跟進:

          private void sched(TimerTask task, long time, long period) {        //省略非重點代碼        synchronized(queue) {            if (!thread.newTasksMayBeScheduled)                throw new IllegalStateException("Timer already cancelled.");            synchronized(task.lock) {                if (task.state != TimerTask.VIRGIN)                    throw new IllegalStateException(                        "Task already scheduled or cancelled");                task.nextExecutionTime = time;                task.period = period;                task.state = TimerTask.SCHEDULED;            }            queue.add(task);            if (queue.getMin() == task)                queue.notify();        }    }

          這里其實做了兩個事情

          • 給task設(shè)定了一些參數(shù),類似于初始化task。這里還給它加了把鎖,可以思考一下為甚要在此初始化?為何要加鎖?(不是本文范疇,各位伙伴自行思考)

          • 把初始化后的task加入到queue中。

          讀到這里,我們還是沒有看到到底是如何實現(xiàn)定時的?別著急,繼續(xù)。進入queu.add(task)

          /**    * Adds a new task to the priority queue.    */   void add(TimerTask task) {       // Grow backing store if necessary       if (size + 1 == queue.length)           queue = Arrays.copyOf(queue, 2*queue.length);       queue[++size] = task;       fixUp(size);   }

          這里注釋提到,加入一個新任務(wù)到優(yōu)先級隊列中去。其實這里的TimerTask[]是一個優(yōu)先級隊列,使用數(shù)組存儲方式。并且它的數(shù)據(jù)結(jié)構(gòu)是heap。包括從fixUp()我們也能看出來,它是在保持堆屬性,即堆化(heapify)。

          那么能分析的都分析完了,還是沒能看到定時是如何實現(xiàn)的?再次靜下來想一想,定時任務(wù)如果想執(zhí)行,首先得啟動定時器。所有咱們再次關(guān)注構(gòu)造方法。

          Timer一共有4個構(gòu)造方法,看最底層的:

          public Timer(String name) {        thread.setName(name);        thread.start();    }

          可以看到,這里在啟動一個thread,那么既然是一個Thread,那肯定就得關(guān)注它的 run()方法了。進入:

          public void run() {        try {            mainLoop();        } finally {            // Someone killed this Thread, behave as if Timer cancelled            synchronized(queue) {                newTasksMayBeScheduled = false;                queue.clear();  // Eliminate obsolete references            }        }    }

          繼續(xù)進入mainLoop():

          /**
               * The main timer loop.  (See class comment.)
               */
              private void mainLoop() {
                  while (true) {
                      try {
                          TimerTask task;
                          boolean taskFired;
                          synchronized(queue) {
                              //省略
                              long currentTime, executionTime;
                              task = queue.getMin();
                              synchronized(task.lock) {
                                  if (task.state == TimerTask.CANCELLED) {
                                      queue.removeMin();
                                      continue;  // No action required, poll queue again
                                  }
                                  currentTime = System.currentTimeMillis();
                                  executionTime = task.nextExecutionTime;
                                  if (taskFired = (executionTime<=currentTime)) {
                                      if (task.period == 0) { // Non-repeating, remove
                                          queue.removeMin();
                                          task.state = TimerTask.EXECUTED;
                                      } else { // Repeating task, reschedule
                                          queue.rescheduleMin(
                                            task.period<0 ? currentTime   - task.period
                                                          : executionTime + task.period);
                                      }
                                  }
                              }
                              if (!taskFired) // Task hasn't yet fired; wait
                                  queue.wait(executionTime - currentTime);
                          }
                          if (taskFired)  // Task fired; run it, holding no locks
                              task.run();
                      } catch(InterruptedException e) {
                      }
                  }
              }

          從上述源碼中,可以看出有兩個重要的if

          • if (taskFired = (executionTime<=currentTime)),表示已經(jīng)到了執(zhí)行時間,那么下面執(zhí)行任務(wù)就好了;

          • if (!taskFired),表示未到執(zhí)行時間,那么等待就好了。那么是如何等待的呢?再仔細(xì)一看,原來是調(diào)用了Object.wait(long timeout)。

          到這里我們知道了,原來jdk中的定時器是這樣實現(xiàn)的啊,等待是使用最簡單的Object.wait()實現(xiàn)的??!別著急,這里有個小提問:使用Therad.sleep()可以實現(xiàn)嘛?如果可以,為何不用呢?

          java.util.concurrent.DelayQueue

          比較細(xì)致地分析了java.util.Timer,DelayQueue也大同小異。整理一下心情,重新出發(fā)。

          先上示例代碼:

          DelayQueue它本質(zhì)上是一個隊列,而這個隊列里也只有存放Delayed的子類才有意義,所有定義了DelayTask:

          public class DelayTask implements Delayed {    private Date startDate  = new Date();    public DelayTask(Long delayMillions) {        this.startDate.setTime(new Date().getTime() + delayMillions);    }    @Override    public int compareTo(Delayed o) {        long result = this.getDelay(TimeUnit.NANOSECONDS)                - o.getDelay(TimeUnit.NANOSECONDS);        if (result < 0) {            return -1;        } else if (result > 0) {            return 1;        } else {            return 0;        }    }    @Override    public long getDelay(TimeUnit unit) {        Date now = new Date();        long diff = startDate.getTime() - now.getTime();        return unit.convert(diff, TimeUnit.MILLISECONDS);    }}    public static void main(String[] args) throws Exception {        BlockingQueue<DelayTask> queue = new DelayQueue<>();        DelayTask delayTask = new DelayTask(1000 * 5L);        queue.put(delayTask);        while (queue.size()>0){            queue.take();        }    }

          看main方法,主要做了三件事:

          • 構(gòu)造DelayTask,其中的延遲時間是5秒

          • 將任務(wù)放入隊列

          • 從隊列中取任務(wù)

          DelayQueue跟剛才的Timer.TaskQueue是比較相似的,都是優(yōu)先級隊列,放入元素時,都得堆化(DelayQueue.put()如果元素滿了,會阻塞。自行研究)。重點看queue.take()。

          public E take() throws InterruptedException {        final ReentrantLock lock = this.lock;        lock.lockInterruptibly();        try {            for (;;) {                E first = q.peek();                if (first == null)                    available.await();                else {                    long delay = first.getDelay(NANOSECONDS);                    if (delay <= 0)                        return q.poll();                    first = null; // don't retain ref while waiting                    if (leader != null)                        available.await();                    else {                        Thread thisThread = Thread.currentThread();                        leader = thisThread;                        try {                            available.awaitNanos(delay);                        } finally {                            if (leader == thisThread)                                leader = null;                        }                    }                }            }        } finally {            if (leader == null && q.peek() != null)                available.signal();            lock.unlock();        }    }

          源碼中出現(xiàn)了三次await字眼:

          • 第一次是當(dāng)隊列為空時,等待;

          • 第二次等待是因為,發(fā)現(xiàn)有任務(wù),沒有到執(zhí)行時間,并且有準(zhǔn)備執(zhí)行的線程(leader)。咱們得講理吧,既然已經(jīng)有人在準(zhǔn)備執(zhí)行了,咱們就得等吧。

          • 第三次是真正延時的地方了,available.awaitNanos(delay),此時也沒有別的線程要執(zhí)行,也就是我將要執(zhí)行,所有等待剩下的延遲時間即可。

          這里咱們明白了,DelayQueue的等待是通過Condition.await()來實現(xiàn)的。請注意,這里又有一個小問題了:Object.wait()與Conditon.await()有何異同?

          java.util.concurrent.ScheduledThreadPoolExecutor

          由于ScheduledThreadPoolExecutor涉及到的線程池(ThreadPoolExecutor)內(nèi)容較多,所有就不詳細(xì)分析了,也考慮到讀到這里,難免有些疲倦。直接簡述一下結(jié)論:在創(chuàng)建ScheduledThreadPoolExecutor時,線程池的工作隊列使用的是DelayedWorkQueue,它的take()方法,與DelayQueue.take()方法極其相似,也有三個等待。

          至此,要結(jié)束了??偨Y(jié)一下,jdk中實現(xiàn)定時器一共有兩種方式:

          1. 使用Object.wait()

          2. 使用Conditon.await()

          還記得文中的兩個小提問嘛:

          1. 使用Thread.sleep()可以實現(xiàn)嘛?如果可以,為何不用呢?

          2. Object.wait()與Conditon.await()有何異同?

          作者:Fooisart
          來源:https://www.jianshu.com/p/e21eb60a2c41

          最近給大家找了  零基礎(chǔ)學(xué)小程序


          資源,怎么領(lǐng)?。?/span>


          掃二維碼為,加我微信,回復(fù):基礎(chǔ)學(xué)小程序

           注意,不要亂回復(fù) 


          沒錯,不是機器人
          記得一定要等待,等待才有好東西
          瀏覽 62
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91爱爱网| 国产在线激情视频 | 欧美高清无码在线观看 | 国产精品久久电影网 | 国产又黄又爽又粗又大免费视频 |