寫一個定時自檢

有一個後臺程序需要這麼一個功能:
需要知道自己什麼時候進入穩定狀態

我原來用一種沙雕方式實現的,即開一個線程讓一個計數器定時自增,自增到某個數即表示已經進入穩定狀態,如果中間有打破這種狀態的操作,那麼就讓計數器重置爲0:

	public static AtomicInteger STABLE_TIMER = new AtomicInteger(0);
	
		taskExecutors.execute(new Runnable() {
			@Override
			public void run() {
				while (true) {
					try {
						TimeUnit.SECONDS.sleep(1);
						if (STABLE_TIMER.getAndIncrement() == LC.STABLE_MIN_TIME) {
							L.i(Thread.currentThread().getName() + "|進入穩定狀態,報告自身狀態");
							//TODO 進入穩定狀態需要做的操作
						}
					} catch (InterruptedException e) {
						e.printStackTrace();
						STABLE_TIMER.decrementAndGet();
					}
				}
			}
		});

雖然勉強達到了效果,但實在是…
太醜了

後來想了想,其實可以找到一些類比場景,比如輪詢,比如心跳;

再仔細想想,原來看過的netty心跳源碼中有一段和這個需求非常相似,比如讀取超時的判斷就依賴一個ReaderIdleTimeoutTask

    ScheduledFuture<?> schedule(ChannelHandlerContext ctx, Runnable task, long delay, TimeUnit unit) {
        return ctx.executor().schedule(task, delay, unit);
    }

    private final class ReaderIdleTimeoutTask extends AbstractIdleTask {

        ReaderIdleTimeoutTask(ChannelHandlerContext ctx) {
            super(ctx);
        }

        @Override
        protected void run(ChannelHandlerContext ctx) {
            long nextDelay = readerIdleTimeNanos;
            if (!reading) {
                nextDelay -= ticksInNanos() - lastReadTime;
            }

            if (nextDelay <= 0) {
                // Reader is idle - set a new timeout and notify the callback.
                readerIdleTimeout = schedule(ctx, this, readerIdleTimeNanos, TimeUnit.NANOSECONDS);

                boolean first = firstReaderIdleEvent;
                firstReaderIdleEvent = false;

                try {
                    IdleStateEvent event = newIdleStateEvent(IdleState.READER_IDLE, first);        
                    channelIdle(ctx, event);
                } catch (Throwable t) {
                    ctx.fireExceptionCaught(t);
                }
            } else {
                // Read occurred before the timeout - set a new timeout with shorter delay.
                readerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS);
            }
        }
    }

其中channelIdle方法就相當於進入了一種判定狀態,在這裏當然是判定爲超時了。

所以Netty的心跳就是這樣:定時自檢是否已經達到一種“超時狀態“,如果當前沒有,那麼就上次的時間進行計算,再決定下一次自檢的時間。

那麼可以直接仿寫一個task,整一個工具類出來:

public class StableStateHolder {

	private static class Holder {
		static StableStateHolder INSTANCE = new StableStateHolder();
	}

	public static StableStateHolder get() {
		return Holder.INSTANCE;
	}

	private ScheduledExecutorService stableTimer;

	private ScheduledFuture<?> stableTimeout;

	private static final long DELAY = 10 * 1000;
	private static final long DELAY_LONG = 20 * 1000;

	private long currentTimeMills;

	private StableStateHolder() {
		stableTimer = new ScheduledThreadPoolExecutor(1);
		stableTimeout = schedule(new StableTimeoutTask(), DELAY_LONG, TimeUnit.MILLISECONDS);
		currentTimeMills = System.currentTimeMillis();
	}

	public void tick() {
		currentTimeMills = System.currentTimeMillis();
	}

	private ScheduledFuture<?> schedule(Runnable task, long delay, TimeUnit unit) {
		return stableTimer.schedule(task, delay, unit);
	}

	private final class StableTimeoutTask implements Runnable {

		@Override
		public void run() {
			long prevDelay = System.currentTimeMillis() - currentTimeMills;
			if (prevDelay > DELAY) {
				//TODO 進入判定狀態
				stableTimeout = schedule(this, DELAY, TimeUnit.MILLISECONDS);
			} else {
				stableTimeout = schedule(this, DELAY - prevDelay, TimeUnit.MILLISECONDS);
			}
			L.w("活躍線程數:" + Thread.activeCount());
		}
	}
}

這樣就好看多了,雖然還是有點沙雕。哈哈哈哈。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章