publicHashedWheelTimer(ThreadFactory threadFactory,long tickDuration,TimeUnit unit,int ticksPerWheel,boolean leakDetection,long maxPendingTimeouts){if(threadFactory ==null){thrownewNullPointerException("threadFactory");}if(unit ==null){thrownewNullPointerException("unit");}if(tickDuration <=0){thrownewIllegalArgumentException("tickDuration must be greater than 0: "+ tickDuration);}if(ticksPerWheel <=0){thrownewIllegalArgumentException("ticksPerWheel must be greater than 0: "+ ticksPerWheel);}// Normalize ticksPerWheel to power of two and initialize the wheel.//创建一个HashedWheelBucket[] wheel 2^n次 没一个下面都是HashedWheelBucket 是一个双向链表
wheel =createWheel(ticksPerWheel);// n - 1
mask = wheel.length -1;// Convert tickDuration to nanos.//时间换算成纳秒 100ms -> 100 * (10^6)long duration = unit.toNanos(tickDuration);// 时间不能太长if(duration >=Long.MAX_VALUE/ wheel.length){thrownewIllegalArgumentException(String.format("tickDuration: %d (expected: 0 < tickDuration in nanos < %d",
tickDuration,Long.MAX_VALUE/ wheel.length));}//最低1纳秒if(duration <MILLISECOND_NANOS){if(logger.isWarnEnabled()){
logger.warn("Configured tickDuration %d smaller then %d, using 1ms.",
tickDuration,MILLISECOND_NANOS);}this.tickDuration =MILLISECOND_NANOS;}else{this.tickDuration = duration;}//workerThread 单线程用于处理所有的定时任务,它会在每个tick执行一个bucket中所有的定时任务,//以及一些其他的操作。意味着定时任务不能有较大的阻塞和耗时,不然就会影响定时任务执行的准时性和有效性。//private final Thread workerThread;//private final Worker worker = new Worker(); 是一个runable
workerThread = threadFactory.newThread(worker);//内存泄漏检测
leak = leakDetection ||!workerThread.isDaemon()? leakDetector.track(this):null;//当时间轮上的定时任务数量超过该值就报警this.maxPendingTimeouts = maxPendingTimeouts;//如果创建的时间轮对象超过64个,也会报警。一个时间轮就是一个线程,线程太多也会影响性能if(INSTANCE_COUNTER.incrementAndGet()>INSTANCE_COUNT_LIMIT&&WARNED_TOO_MANY_INSTANCES.compareAndSet(false,true)){reportTooManyInstances();}}
newTimeout放入一个timerTask
//task一个runable//delay 延迟多少秒//unit 时间单位publicTimeoutnewTimeout(TimerTask task,long delay,TimeUnit unit){if(task ==null){thrownewNullPointerException("task");}if(unit ==null){thrownewNullPointerException("unit");}//添加任务之后,等待执行的任务加1long pendingTimeoutsCount = pendingTimeouts.incrementAndGet();//任务是否过多if(maxPendingTimeouts >0&& pendingTimeoutsCount > maxPendingTimeouts){
pendingTimeouts.decrementAndGet();thrownewRejectedExecutionException("Number of pending timeouts ("+ pendingTimeoutsCount +") is greater than or equal to maximum allowed pending "+"timeouts ("+ maxPendingTimeouts +")");}//启动工作线程,并且确保只启动一次,这里面会设计线程的等待和唤醒start();//下面的代码保证startTime有值// Add the timeout to the timeout queue which will be processed on the next tick.// During processing all the queued HashedWheelTimeouts will be added to the correct HashedWheelBucket.long deadline =System.nanoTime()+ unit.toNanos(delay)- startTime;// Guard against overflow.if(delay >0&& deadline <0){
deadline =Long.MAX_VALUE;}//包装任务HashedWheelTimeout timeout =newHashedWheelTimeout(this, task, deadline);//放任务,后面的看定时任务
timeouts.add(timeout);return timeout;}
start()
publicvoidstart(){//判断时间轮的工作状态switch(WORKER_STATE_UPDATER.get(this)){caseWORKER_STATE_INIT://cas更新到开始状态if(WORKER_STATE_UPDATER.compareAndSet(this,WORKER_STATE_INIT,WORKER_STATE_STARTED)){//启动work线程,该线程一旦启动,就会执行任务,所以核心在work线程要执行的runable的run方法内
workerThread.start();}break;//如果启动了就什么也不做caseWORKER_STATE_STARTED:break;//如果状态是结束,就抛出异常caseWORKER_STATE_SHUTDOWN:thrownewIllegalStateException("cannot be started once stopped");default:thrownewError("Invalid WorkerState");}//这里会暂停一卡,因为要等待work线程启动完全,并且starttime被赋值成功while(startTime ==0){try{
startTimeInitialized.await();}catch(InterruptedException ignore){// Ignore - it will be ready very soon.}}}
netty时间轮重要代码
worker.run()
publicvoidrun(){// Initialize the startTime.// 初始化开始的时间
startTime =System.nanoTime();if(startTime ==0){// We use 0 as an indicator for the uninitialized value here, so make sure it's not 0 when initialized.
startTime =1;}// Notify the other threads waiting for the initialization at start().//第一个人在 await()
startTimeInitialized.countDown();//这边有点问题,如果这个定时任务一个地方晚了,所有的地方就会晚do{//到下一个时间点finallong deadline =waitForNextTick();//如果返回的时间>0if(deadline >0){int idx =(int)(tick & mask);//获得所有取消的任务,刹车农户processCancelledTasks();//获得具体的bucket里面都是任务HashedWheelBucket bucket =
wheel[idx];//转移任务到具体的bucket里面transferTimeoutsToBuckets();//开始执行需要执行的任务
bucket.expireTimeouts(deadline);//tick++到下个wheel的区间
tick++;}//如果不是WORKER_STATE_STARTED就不循环了}while(WORKER_STATE_UPDATER.get(HashedWheelTimer.this)==WORKER_STATE_STARTED);// Fill the unprocessedTimeouts so we can return them from stop() method.//所有没有执行的放到unprocessedTimeouts这个set里面for(HashedWheelBucket bucket: wheel){
bucket.clearTimeouts(unprocessedTimeouts);}for(;;){//还没有提交的也拿出来HashedWheelTimeout timeout = timeouts.poll();if(timeout ==null){break;}if(!timeout.isCancelled()){//如果没有cancel的放入到unprocessedTimeouts里面
unprocessedTimeouts.add(timeout);}}processCancelledTasks();}
Timeout.cancel() 没有直接取消
publicbooleancancel(){// only update the state it will be removed from HashedWheelBucket on next tick.if(!compareAndSetState(ST_INIT,ST_CANCELLED)){returnfalse;}// If a task should be canceled we put this to another queue which will be processed on each tick.// So this means that we will have a GC latency of max. 1 tick duration which is good enough. This way// we can make again use of our MpscLinkedQueue and so minimize the locking / overhead as much as possible.//队列里面添加
timer.cancelledTimeouts.add(this);returntrue;}
privatevoidprocessCancelledTasks(){for(;;){//canel的取消掉,从双向链表里面干掉HashedWheelTimeout timeout = cancelledTimeouts.poll();if(timeout ==null){// all processedbreak;}try{//双向链表里面干掉
timeout.remove();}catch(Throwable t){if(logger.isWarnEnabled()){
logger.warn("An exception was thrown while process a cancellation task", t);}}}}
privatelongwaitForNextTick(){long deadline = tickDuration *(tick +1);for(;;){finallong currentTime =System.nanoTime()- startTime;long sleepTimeMs =(deadline - currentTime +999999)/1000000;//说明已经上一个了if(sleepTimeMs <=0){if(currentTime ==Long.MIN_VALUE){return-Long.MAX_VALUE;}else{return currentTime;}}// Check if we run on windows, as if thats the case we will need// to round the sleepTime as workaround for a bug that only affect// the JVM if it runs on windows.//// See https://github.com/netty/netty/issues/356if(PlatformDependent.isWindows()){
sleepTimeMs = sleepTimeMs /10*10;}try{Thread.sleep(sleepTimeMs);}catch(InterruptedException ignored){//响应中断,如果状态是WORKER_STATE_SHUTDOWN,返回Long.MIN_VALUE外面判断<0if(WORKER_STATE_UPDATER.get(HashedWheelTimer.this)==WORKER_STATE_SHUTDOWN){returnLong.MIN_VALUE;}}}}
privatevoidtransferTimeoutsToBuckets(){// transfer only max. 100000 timeouts per tick to prevent a thread to stale the workerThread when it just// adds new timeouts in a loop.for(int i =0; i <100000; i++){HashedWheelTimeout timeout = timeouts.poll();if(timeout ==null){// all processedbreak;}if(timeout.state()==HashedWheelTimeout.ST_CANCELLED){// Was cancelled in the meantime.continue;}long calculated = timeout.deadline / tickDuration;//计算一下我有多少圈,假设我时间4S一圈,每个格子1S,我现在是14S ,remainingRounds = 3,然后落到(15 - 3*4 = 2,下标为1)位置的地方
timeout.remainingRounds =(calculated - tick)/ wheel.length;finallong ticks =Math.max(calculated, tick);// Ensure we don't schedule for past.int stopIndex =(int)(ticks & mask);//获得具体的,放入到bucket下面HashedWheelBucket bucket = wheel[stopIndex];
bucket.addTimeout(timeout);}}
expireTimeouts执行具体方法的地方
publicvoidexpireTimeouts(long deadline){HashedWheelTimeout timeout = head;// process all timeoutswhile(timeout !=null){HashedWheelTimeout next = timeout.next;if(timeout.remainingRounds <=0){//如果<=0 从HashedWheelTimeout移除
next =remove(timeout);if(timeout.deadline <= deadline){//执行task任务
timeout.expire();}else{//说明时间轮有问题// The timeout was placed into a wrong slot. This should never happen.thrownewIllegalStateException(String.format("timeout.deadline (%d) > deadline (%d)", timeout.deadline, deadline));}//判断是否是cancel的在执行中,直接remove}elseif(timeout.isCancelled()){
next =remove(timeout);}else{//圈数-1
timeout.remainingRounds --;}//到下一个,继续循环
timeout = next;}}
stop()方法
publicSet<Timeout>stop(){if(Thread.currentThread()== workerThread){thrownewIllegalStateException(HashedWheelTimer.class.getSimpleName()+".stop() cannot be called from "+TimerTask.class.getSimpleName());}if(!WORKER_STATE_UPDATER.compareAndSet(this,WORKER_STATE_STARTED,WORKER_STATE_SHUTDOWN)){// workerState can be 0 or 2 at this moment - let it always be 2.if(WORKER_STATE_UPDATER.getAndSet(this,WORKER_STATE_SHUTDOWN)!=WORKER_STATE_SHUTDOWN){INSTANCE_COUNTER.decrementAndGet();if(leak !=null){boolean closed = leak.close(this);assert closed;}}returnCollections.emptySet();}try{boolean interrupted =false;while(workerThread.isAlive()){
workerThread.interrupt();try{
workerThread.join(100);}catch(InterruptedException ignored){
interrupted =true;}}if(interrupted){Thread.currentThread().interrupt();}}finally{INSTANCE_COUNTER.decrementAndGet();if(leak !=null){boolean closed = leak.close(this);assert closed;}}//返回我们上面说的那个如果跳出那个循环都是时候return worker.unprocessedTimeouts();}