【JUC】DelayQueue瞭解一下

DelayQueue 對我來說還有點陌生,這可能也是我第一次使用。

talk is cheap, show me the code.

單線程消費

package com.itplh.queue;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * 使用延時隊列DelayQueue,按時間順序消費任務
 *
 * @author: tanpeng
 * @since: 2020-06-08 15:55
 */
 @Slf4j
public class TestDelayQueue {

    public static void main(String[] args) {
        // 現在的時間
        long now = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
        // 過去的時間
        long ago = now - 10L;
        // 未來的時間
        long future = now + 10L;

        DelayQueue<MyTask> delayQueue = new DelayQueue();
        delayQueue.put(new MyTask("task1-now", now));
        delayQueue.put(new MyTask("task2-ago", ago));
        delayQueue.put(new MyTask("task3-future", future));

        MyTask task = null;
        while (true) {
            // 檢查隊首元素
            MyTask checkTask = delayQueue.peek();
            if (checkTask == null) {
                break;
            }
            // 到了任務執行時間,則消費該任務
            LocalDateTime taskExecutorTime = LocalDateTime.ofEpochSecond(checkTask.getTime(), 0, ZoneOffset.of("+8"));
            if (LocalDateTime.now().isAfter(taskExecutorTime)) {
                task = delayQueue.poll();
                log.info("消費了:{}", task);
                continue;
            }
            // 執行到這裏了說明未到任務的執行時間,sleep2秒後再去繼續消費任務
            try {
                log.info("...");
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
}

@Data
class MyTask implements Delayed {

    private String taskId;
    private long time;

    public MyTask(String taskId, long time) {
        this.taskId = taskId;
        this.time = time;
    }

    @Override
    public long getDelay(TimeUnit unit) {
        return time - System.currentTimeMillis();
    }

    @Override
    public int compareTo(Delayed o) {
        MyTask task = (MyTask) o;
        long diff = this.time - task.getTime();
        if (diff <= 0) {
            return -1;
        }
        return 1;
    }
}

控制檯輸出

12:44:31.700 [main] INFO com.itplh.queue.TestDelayQueue - 消費了:MyTask(taskId=task2-ago, time=1592282661)
12:44:31.704 [main] INFO com.itplh.queue.TestDelayQueue - 消費了:MyTask(taskId=task1-now, time=1592282671)
12:44:31.704 [main] INFO com.itplh.queue.TestDelayQueue - ...
12:44:33.704 [main] INFO com.itplh.queue.TestDelayQueue - ...
12:44:35.704 [main] INFO com.itplh.queue.TestDelayQueue - ...
12:44:37.704 [main] INFO com.itplh.queue.TestDelayQueue - ...
12:44:39.704 [main] INFO com.itplh.queue.TestDelayQueue - ...
12:44:41.704 [main] INFO com.itplh.queue.TestDelayQueue - 消費了:MyTask(taskId=task3-future, time=1592282681)

從上面的demo可以得到結論:

  • 消費任務是按照時間的先後順序的。
  • 時間未到的任務是不會被提前消費的,只有到了該任務的時間點纔會被消費。

多線程消費

package com.itplh.queue;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.concurrent.*;
import java.util.concurrent.atomic.LongAdder;

/**
 * 使用延時隊列DelayQueue,按時間順序消費任務
 * 多線程版本
 * @author: tanpeng
 * @since: 2020-06-16 11:05
 */
@Slf4j
public class TestDelayQueue {

    private static int total = 1000;
    private static DelayQueue<MyTask> delayQueue = new DelayQueue();
    private static CountDownLatch countDownLatch = new CountDownLatch(total);
    private static LongAdder longAdder = new LongAdder();

    public static void main(String[] args) throws InterruptedException {

		// 向隊列內放元素
        long now = System.currentTimeMillis();
        for (int i = 1; i <= total; i++) {
            long time = now + Long.valueOf(RandomStringUtils.randomNumeric(4));
            delayQueue.put(new MyTask("task" + i, time));
        }

        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            executorService.submit(() -> consumer(delayQueue));
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("{}", longAdder.sum());
    }

    private static void consumer(DelayQueue<MyTask> delayQueue) {
        while (true) {
            // 保證原子性,檢查的元素與消費的元素是同一個
            synchronized ("") {
                // 檢查隊首元素
                MyTask checkTask = delayQueue.peek();
                if (checkTask == null) {
                    break;
                }
                // 到了任務執行時間,則消費該任務
                Instant instant = new Date(checkTask.getTime()).toInstant();
                LocalDateTime taskExecutorTime = LocalDateTime.ofInstant(instant, ZoneOffset.of("+8"));
                if (LocalDateTime.now().isAfter(taskExecutorTime)) {
                    MyTask task = delayQueue.poll();
                    log.info(" 消費了:" + task);
                    countDownLatch.countDown();
                    longAdder.add(1);
                    continue;
                }
            }
            // 執行到這裏了說明未到任務的執行時間,sleep2秒後再去繼續消費任務
            try {
                log.info("...");
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

控制檯輸出

12:20:09.145 [pool-1-thread-1] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task207, time=1592281209106)
12:20:09.148 [pool-1-thread-1] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task929, time=1592281209147)
12:20:09.148 [pool-1-thread-1] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:09.148 [pool-1-thread-5] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:09.148 [pool-1-thread-4] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:09.148 [pool-1-thread-3] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:09.148 [pool-1-thread-2] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:11.148 [pool-1-thread-3] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task294, time=1592281209148)
12:20:11.148 [pool-1-thread-3] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task944, time=1592281209187)
12:20:11.150 [pool-1-thread-4] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task350, time=1592281209189)
12:20:11.150 [pool-1-thread-4] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task919, time=1592281209190)
12:20:11.160 [pool-1-thread-4] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:11.160 [pool-1-thread-2] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:11.160 [pool-1-thread-5] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:11.160 [pool-1-thread-1] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:11.160 [pool-1-thread-3] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:13.160 [pool-1-thread-3] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task809, time=1592281211162)
12:20:13.160 [pool-1-thread-3] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task7, time=1592281211162)
12:20:13.167 [pool-1-thread-5] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task357, time=1592281213163)
12:20:13.167 [pool-1-thread-5] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:13.167 [pool-1-thread-4] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:13.167 [pool-1-thread-2] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:13.167 [pool-1-thread-1] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:13.167 [pool-1-thread-3] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:15.167 [pool-1-thread-1] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task247, time=1592281213169)
12:20:15.167 [pool-1-thread-1] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task505, time=1592281213176)
12:20:15.167 [pool-1-thread-1] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task548, time=1592281213185)
12:20:15.172 [pool-1-thread-4] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task519, time=1592281215151)
12:20:15.172 [pool-1-thread-4] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:15.172 [pool-1-thread-2] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:15.172 [pool-1-thread-3] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:15.172 [pool-1-thread-5] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:15.173 [pool-1-thread-1] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:17.172 [pool-1-thread-4] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task596, time=1592281215173)
12:20:17.172 [pool-1-thread-4] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task949, time=1592281215191)
12:20:17.176 [pool-1-thread-5] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task577, time=1592281217171)
12:20:17.176 [pool-1-thread-5] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:17.176 [pool-1-thread-2] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:17.176 [pool-1-thread-3] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:17.176 [pool-1-thread-1] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:17.176 [pool-1-thread-4] INFO com.itplh.queue.TestDelayQueue -  ...
12:20:19.178 [pool-1-thread-5] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task690, time=1592281219072)
12:20:19.178 [pool-1-thread-5] INFO com.itplh.queue.TestDelayQueue -  消費了:MyTask(taskId=task703, time=1592281219083)
12:20:19.179 [main] INFO com.itplh.queue.TestDelayQueue - 1000
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章