JAVA数据结构与算法之延迟队列(二):HashedWheelTimer

HashedWheelTimer

NettyHashedWheelTimer类基于时间轮算法实现

主要成员变量
private final Worker worker = new Worker(); // 任务类,继承Runnable接口
private final Thread workerThread; // 工作线程,用来执行worker任务
private volatile int workerState; // 任务状态,cas自旋和volatile保证原子操作,0-初始化中, 1-开启, 2-关闭
private final long tickDuration; // wheel上每个时间片执行的时间间隔
private final HashedWheelBucket[] wheel; // 时间轮,HashedWheelBucket内部由双向队列实现
private final int mask;
private final CountDownLatch startTimeInitialized = new CountDownLatch(1); // 用于等待任务启动完成
private final Queue<HashedWheelTimeout> timeouts = PlatformDependent.newMpscQueue(); // 添加的任务
private final Queue<HashedWheelTimeout> cancelledTimeouts = PlatformDependent.newMpscQueue(); // 取消的任务
private final AtomicLong pendingTimeouts = new AtomicLong(0); // 等待的任务数
private final long maxPendingTimeouts; // 最大等待任务数

private volatile long startTime; // 延迟任务的开始时间
构造函数
	public HashedWheelTimer(
            ThreadFactory threadFactory,
            long tickDuration, TimeUnit unit, int ticksPerWheel, boolean leakDetection,
            long maxPendingTimeouts) {
        // 线程工厂,用来创建worker线程
        if (threadFactory == null) {
            throw new NullPointerException("threadFactory");
        }
        // 每个tick运行的时间间隔单位
        if (unit == null) {
            throw new NullPointerException("unit");
        }
        // 每个tick运行的时间间隔
        if (tickDuration <= 0) {
            throw new IllegalArgumentException("tickDuration must be greater than 0: " + tickDuration);
        }
        // wheel数组即时间轮的大小
        if (ticksPerWheel <= 0) {
            throw new IllegalArgumentException("ticksPerWheel must be greater than 0: " + ticksPerWheel);
        }
        // 将wheel数组大小标准化为2的n次方,同时初始化wheel数组
        wheel = createWheel(ticksPerWheel);
        // 掩码
        mask = wheel.length - 1;

        // Convert tickDuration to nanos.
        this.tickDuration = unit.toNanos(tickDuration);

        // 防止溢出
        if (this.tickDuration >= Long.MAX_VALUE / wheel.length) {
            throw new IllegalArgumentException(String.format(
                    "tickDuration: %d (expected: 0 < tickDuration in nanos < %d",
                    tickDuration, Long.MAX_VALUE / wheel.length));
        }
        // 创建工作线程
        workerThread = threadFactory.newThread(worker);
        // 是否泄漏监控
        leak = leakDetection || !workerThread.isDaemon() ? leakDetector.track(this) : null;
        // 最大等待任务数
        this.maxPendingTimeouts = maxPendingTimeouts;
        // 警告,限制HashedWheelTimer实例数为INSTANCE_COUNT_LIMIT个即64个,预防过多的线程影响性能
        if (INSTANCE_COUNTER.incrementAndGet() > INSTANCE_COUNT_LIMIT &&
            WARNED_TOO_MANY_INSTANCES.compareAndSet(false, true)) {
            reportTooManyInstances();
        }
    }
创建时间轮函数createWheel()
	private static HashedWheelBucket[] createWheel(int ticksPerWheel) {
        if (ticksPerWheel <= 0) {
            throw new IllegalArgumentException(
                    "ticksPerWheel must be greater than 0: " + ticksPerWheel);
        }
        if (ticksPerWheel > 1073741824) {
            throw new IllegalArgumentException(
                    "ticksPerWheel may not be greater than 2^30: " + ticksPerWheel);
        }
        // 严格控制时间轮的大小为2的n次方
        ticksPerWheel = normalizeTicksPerWheel(ticksPerWheel);
        // 初始化时间轮数组并赋值
        HashedWheelBucket[] wheel = new HashedWheelBucket[ticksPerWheel];
        for (int i = 0; i < wheel.length; i ++) {
            wheel[i] = new HashedWheelBucket();
        }
        return wheel;
    }
    // 大于指定值的最小2的n次方
    private static int normalizeTicksPerWheel(int ticksPerWheel) {
        int normalizedTicksPerWheel = 1;
        while (normalizedTicksPerWheel < ticksPerWheel) {
            normalizedTicksPerWheel <<= 1;
        }
        return normalizedTicksPerWheel;
    }
添加任务函数newTimeout()

添加任务时,如果worker线程没启动则启动,同时向timeouts队列中生产timeout任务

	public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) {
        if (task == null) {
            throw new NullPointerException("task");
        }
        if (unit == null) {
            throw new NullPointerException("unit");
        }
        // 等待任务数+1
        long pendingTimeoutsCount = pendingTimeouts.incrementAndGet();
        // 超过最大等待任务数,拒绝加入
        if (maxPendingTimeouts > 0 && pendingTimeoutsCount > maxPendingTimeouts) {
            pendingTimeouts.decrementAndGet();
            throw new RejectedExecutionException("Number of pending timeouts ("
                + pendingTimeoutsCount + ") is greater than or equal to maximum allowed pending "
                + "timeouts (" + maxPendingTimeouts + ")");
        }
        // 开始任务
        start();
        // 延迟执行的时间
        long deadline = System.nanoTime() + unit.toNanos(delay) - startTime;
        if (delay > 0 && deadline < 0) {
            deadline = Long.MAX_VALUE;
        }
        // 添加到timeouts队列,这是一个MPSC队列,MPSC队列是多生产者单消费者无锁的并发队列,worker线程会对队列进行消费
        HashedWheelTimeout timeout = new HashedWheelTimeout(this, task, deadline);
        timeouts.add(timeout);
        return timeout;
    }

这里看下start()函数如何保证启动一次

	public void start() {
		// 这里使用CAS算法确保原子操作,因此多个线程并发执行只有一次会执行成功
        switch (WORKER_STATE_UPDATER.get(this)) {
            case WORKER_STATE_INIT:
                if (WORKER_STATE_UPDATER.compareAndSet(this, WORKER_STATE_INIT, WORKER_STATE_STARTED)) {
                    workerThread.start();
                }
                break;
            case WORKER_STATE_STARTED:
                break;
            case WORKER_STATE_SHUTDOWN:
                throw new IllegalStateException("cannot be started once stopped");
            default:
                throw new Error("Invalid WorkerState");
        }

        // 等待worker线程初始化一些启动工作
        while (startTime == 0) {
            try {
                startTimeInitialized.await();
            } catch (InterruptedException ignore) {
                // Ignore - it will be ready very soon.
            }
        }
    }

核心逻辑在Worker内部类中

private final Set<Timeout> unprocessedTimeouts = new HashSet<Timeout>();
private long tick; // tick计数
run方法

核心逻辑

	public void run() {
        // 初始化startTime变量
        startTime = System.nanoTime();
        if (startTime == 0) {
            // 初始化失败时的值
            startTime = 1;
        }
        // 通知其他线程继续执行
        startTimeInitialized.countDown();
        do {
        	// 等待直到当前tick时间点到
            final long deadline = waitForNextTick();
            // 当前tick时间点到了
            if (deadline > 0) {
          		// 对应时间轮中的索引
                int idx = (int) (tick & mask);
                // 处理取消的任务
                processCancelledTasks();
                // 当前tick需要处理的bucket
                HashedWheelBucket bucket =
                        wheel[idx];
                // 将timeouts队列移到bucket中
                transferTimeoutsToBuckets();
                // 处理掉当前bucket时间到或过期的任务
                bucket.expireTimeouts(deadline);
                // tick数+1,继续等待执行下一个tick时间点
                tick++;
            }
        } while (WORKER_STATE_UPDATER.get(HashedWheelTimer.this) == WORKER_STATE_STARTED);

        // Fill the unprocessedTimeouts so we can return them from stop() method.
        for (HashedWheelBucket bucket: wheel) {
            bucket.clearTimeouts(unprocessedTimeouts);
        }
        for (;;) {
            HashedWheelTimeout timeout = timeouts.poll();
            if (timeout == null) {
                break;
            }
            if (!timeout.isCancelled()) {
                unprocessedTimeouts.add(timeout);
            }
        }
        processCancelledTasks();
    }
waitForNextTick()

等待下一个tick时间点到来

	private long waitForNextTick() {
// 当前tick需要等待时间
        long deadline = tickDuration * (tick + 1);
        for (;;) {
        	// 当前时间与开始时间的时间间隔
            final long currentTime = System.nanoTime() - startTime;
            // 剩余需要等待的时间
            long sleepTimeMs = (deadline - currentTime + 999999) / 1000000;
            // 当前时间已经超过当前tick执行所需等待的时间了,则返回,否则休眠后再判断
            if (sleepTimeMs <= 0) {
                if (currentTime == Long.MIN_VALUE) {
                    return -Long.MAX_VALUE;
                } else {
                    return currentTime;
                }
            }
            // windows系统bug
            if (PlatformDependent.isWindows()) {
                sleepTimeMs = sleepTimeMs / 10 * 10;
            }
            // 休眠
            try {
                Thread.sleep(sleepTimeMs);
            } catch (InterruptedException ignored) {
            	// HashedWheelTimer shutdown导致的线程中断
                if (WORKER_STATE_UPDATER.get(HashedWheelTimer.this) == WORKER_STATE_SHUTDOWN) {
                    return Long.MIN_VALUE;
                }
            }
        }
    }
transferTimeoutsToBuckets()

每次提取最多10000个任务,避免过多的时间消耗在这里影响定时任务,然后将任务放到对应的tick所在的wheel数组中

	private void transferTimeoutsToBuckets() {
        for (int i = 0; i < 100000; i++) {
            HashedWheelTimeout timeout = timeouts.poll();
            if (timeout == null) {
                // all processed
                break;
            }
            if (timeout.state() == HashedWheelTimeout.ST_CANCELLED) {
                // Was cancelled in the meantime.
                continue;
            }
            // 需要的tick数
            long calculated = timeout.deadline / tickDuration;
            // 执行该任务的时间轮数
            timeout.remainingRounds = (calculated - tick) / wheel.length;
            // 如果需要的tick数小于当前tick,说明时间点已经过了,直接在当前tick执行
            final long ticks = Math.max(calculated, tick);
            // 对应的wheel位置 
            int stopIndex = (int) (ticks & mask);
            // 加入到双向队列
            HashedWheelBucket bucket = wheel[stopIndex];
            bucket.addTimeout(timeout);
        }
    }
HashedWheelBucket.expireTimeouts()

执行过期任务

 	public void expireTimeouts(long deadline) {
        HashedWheelTimeout timeout = head;

        // process all timeouts
        while (timeout != null) {
            HashedWheelTimeout next = timeout.next;
            if (timeout.remainingRounds <= 0) {
                next = remove(timeout);
                // 移除并执行
                if (timeout.deadline <= deadline) {
                	// 这里会执行该任务
                    timeout.expire();
                } else {
                    // The timeout was placed into a wrong slot. This should never happen.
                    throw new IllegalStateException(String.format(
                            "timeout.deadline (%d) > deadline (%d)", timeout.deadline, deadline));
                }
            } else if (timeout.isCancelled()) {
                next = remove(timeout);
            } else {
                timeout.remainingRounds --;
            }
            timeout = next;
        }
    }
processCancelledTasks()

移除取消的任务

	private void processCancelledTasks() {
       for (;;) {
            HashedWheelTimeout timeout = cancelledTimeouts.poll();
            if (timeout == null) {
                // all processed
                break;
            }
            try {
                timeout.remove();
            } catch (Throwable t) {
                if (logger.isWarnEnabled()) {
                    logger.warn("An exception was thrown while process a cancellation task", t);
                }
            }
        }
    }
    void remove() {
        HashedWheelBucket bucket = this.bucket;
        if (bucket != null) {
            bucket.remove(this);
        } else {
            timer.pendingTimeouts.decrementAndGet();
        }
    }
        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值