Java时间轮算法的实现代码示例

考虑这样一个场景,现在有5000个任务,要让这5000个任务每隔5分中触发某个操作,怎么去实现这个需求。大部分人首先想到的是使用定时器,但是5000个任务,你就要用5000个定时器,一个定时器就是一个线程,你懂了吧,这种方法肯定是不行的。

针对这个场景,催生了时间轮算法,时间轮到底是什么?我一贯的风格,自行谷歌去。大发慈悲,发个时间轮介绍你们看看,看文字和图就好了,代码不要看了,那个文章里的代码运行不起来,时间轮介绍。

看好了介绍,我们就开始动手吧。

开发环境:idea + jdk1.8 + maven

新建一个maven工程

创建如下的目录结构

不要忘了pom.xml中添加netty库

<dependencies>
    <dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-all</artifactId>
      <version>4.1.5.Final</version>
    </dependency>
  </dependencies>

代码如下

Timeout.Java

package com.tanghuachun.timer;
public interface Timeout {
  Timer timer();
  TimerTask task();
  boolean isExpired();
  boolean isCancelled();
  boolean cancel();
}

Timer.java

package com.tanghuachun.timer;
import java.util.Set;
import java.util.concurrent.TimeUnit;

public interface Timer {
  Timeout newTimeout(TimerTask task, long delay, TimeUnit unit, String argv);
  Set<Timeout> stop();
}

TimerTask.java

package com.tanghuachun.timer;
public interface TimerTask {
  void run(Timeout timeout, String argv) throws Exception;
}

TimerWheel.java

/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package com.tanghuachun.timer;
import io.netty.util.*;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.util.Collections;
import java.util.HashSet;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

public class TimerWheel implements Timer {

  static final InternalLogger logger =
      InternalLoggerFactory.getInstance(TimerWheel.class);

  private static final ResourceLeakDetector<TimerWheel> leakDetector = ResourceLeakDetectorFactory.instance()
      .newResourceLeakDetector(TimerWheel.class, 1, Runtime.getRuntime().availableProcessors() * 4L);

  private static final AtomicIntegerFieldUpdater<TimerWheel> WORKER_STATE_UPDATER;
  static {
    AtomicIntegerFieldUpdater<TimerWheel> workerStateUpdater =
        PlatformDependent.newAtomicIntegerFieldUpdater(TimerWheel.class, "workerState");
    if (workerStateUpdater == null) {
      workerStateUpdater = AtomicIntegerFieldUpdater.newUpdater(TimerWheel.class, "workerState");
    }
    WORKER_STATE_UPDATER = workerStateUpdater;
  }

  private final ResourceLeak leak;
  private final Worker worker = new Worker();
  private final Thread workerThread;

  public static final int WORKER_STATE_INIT = 0;
  public static final int WORKER_STATE_STARTED = 1;
  public static final int WORKER_STATE_SHUTDOWN = 2;
  @SuppressWarnings({ "unused", "FieldMayBeFinal", "RedundantFieldInitialization" })
  private volatile int workerState = WORKER_STATE_INIT; // 0 - init, 1 - started, 2 - shut down

  private final long tickDuration;
  private final HashedWheelBucket[] wheel;
  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 volatile long startTime;

  /**
   * Creates a new timer with the default thread factory
   * ({@link Executors#defaultThreadFactory()}), default tick duration, and
   * default number of ticks per wheel.
   */
  public TimerWheel() {
    this(Executors.defaultThreadFactory());
  }

  /**
   * Creates a new timer with the default thread factory
   * ({@link Executors#defaultThreadFactory()}) and default number of ticks
   * per wheel.
   *
   * @param tickDuration  the duration between tick
   * @param unit      the time unit of the {@code tickDuration}
   * @throws NullPointerException   if {@code unit} is {@code null}
   * @throws IllegalArgumentException if {@code tickDuration} is <= 0
   */
  public TimerWheel(long tickDuration, TimeUnit unit) {
    this(Executors.defaultThreadFactory(), tickDuration, unit);
  }

  /**
   * Creates a new timer with the default thread factory
   * ({@link Executors#defaultThreadFactory()}).
   *
   * @param tickDuration  the duration between tick
   * @param unit      the time unit of the {@code tickDuration}
   * @param ticksPerWheel the size of the wheel
   * @throws NullPointerException   if {@code unit} is {@code null}
   * @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0
   */
  public TimerWheel(long tickDuration, TimeUnit unit, int ticksPerWheel) {
    this(Executors.defaultThreadFactory(), tickDuration, unit, ticksPerWheel);
  }

  /**
   * Creates a new timer with the default tick duration and default number of
   * ticks per wheel.
   *
   * @param threadFactory a {@link ThreadFactory} that creates a
   *            background {@link Thread} which is dedicated to
   *            {@link TimerTask} execution.
   * @throws NullPointerException if {@code threadFactory} is {@code null}
   */
  public TimerWheel(ThreadFactory threadFactory) {
    this(threadFactory, 100, TimeUnit.MILLISECONDS);
  }

  /**
   * Creates a new timer with the default number of ticks per wheel.
   *
   * @param threadFactory a {@link ThreadFactory} that creates a
   *            background {@link Thread} which is dedicated to
   *            {@link TimerTask} execution.
   * @param tickDuration  the duration between tick
   * @param unit      the time unit of the {@code tickDuration}
   * @throws NullPointerException   if either of {@code threadFactory} and {@code unit} is {@code null}
   * @throws IllegalArgumentException if {@code tickDuration} is <= 0
   */
  public TimerWheel(
      ThreadFactory threadFactory, long tickDuration, TimeUnit unit) {
    this(threadFactory, tickDuration, unit, 512);
  }

  /**
   * Creates a new timer.
   *
   * @param threadFactory a {@link ThreadFactory} that creates a
   *            background {@link Thread} which is dedicated to
   *            {@link TimerTask} execution.
   * @param tickDuration  the duration between tick
   * @param unit      the time unit of the {@code tickDuration}
   * @param ticksPerWheel the size of the wheel
   * @throws NullPointerException   if either of {@code threadFactory} and {@code unit} is {@code null}
   * @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0
   */
  public TimerWheel(
      ThreadFactory threadFactory,
      long tickDuration, TimeUnit unit, int ticksPerWheel) {
    this(threadFactory, tickDuration, unit, ticksPerWheel, true);
  }

  /**
   * Creates a new timer.
   *
   * @param threadFactory a {@link ThreadFactory} that creates a
   *            background {@link Thread} which is dedicated to
   *            {@link TimerTask} execution.
   * @param tickDuration  the duration between tick
   * @param unit      the time unit of the {@code tickDuration}
   * @param ticksPerWheel the size of the wheel
   * @param leakDetection {@code true} if leak detection should be enabled always, if false it will only be enabled
   *            if the worker thread is not a daemon thread.
   * @throws NullPointerException   if either of {@code threadFactory} and {@code unit} is {@code null}
   * @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0
   */
  public TimerWheel(
      ThreadFactory threadFactory,
      long tickDuration, TimeUnit unit, int ticksPerWheel, boolean leakDetection) {

    if (threadFactory == null) {
      throw new NullPointerException("threadFactory");
    }
    if (unit == null) {
      throw new NullPointerException("unit");
    }
    if (tickDuration <= 0) {
      throw new IllegalArgumentException("tickDuration must be greater than 0: " + tickDuration);
    }
    if (ticksPerWheel <= 0) {
      throw new IllegalArgumentException("ticksPerWheel must be greater than 0: " + ticksPerWheel);
    }

    // Normalize ticksPerWheel to power of two and initialize the wheel.
    wheel = createWheel(ticksPerWheel);
    mask = wheel.length - 1;

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

    // Prevent overflow.
    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.open(this) : null;
  }

  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);
    }

    ticksPerWheel = normalizeTicksPerWheel(ticksPerWheel);
    HashedWheelBucket[] wheel = new HashedWheelBucket[ticksPerWheel];
    for (int i = 0; i < wheel.length; i ++) {
      wheel[i] = new HashedWheelBucket();
    }
    return wheel;
  }

  private static int normalizeTicksPerWheel(int ticksPerWheel) {
    int normalizedTicksPerWheel = 1;
    while (normalizedTicksPerWheel < ticksPerWheel) {
      normalizedTicksPerWheel <<= 1;
    }
    return normalizedTicksPerWheel;
  }

  /**
   * Starts the background thread explicitly. The background thread will
   * start automatically on demand even if you did not call this method.
   *
   * @throws IllegalStateException if this timer has been
   *                {@linkplain #stop() stopped} already
   */
  public void start() {
    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");
    }

    // Wait until the startTime is initialized by the worker.
    while (startTime == 0) {
      try {
        startTimeInitialized.await();
      } catch (InterruptedException ignore) {
        // Ignore - it will be ready very soon.
      }
    }
  }

  @Override
  public Set<Timeout> stop() {
    if (Thread.currentThread() == workerThread) {
      throw new IllegalStateException(
          TimerWheel.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.
      WORKER_STATE_UPDATER.set(this, WORKER_STATE_SHUTDOWN);

      if (leak != null) {
        leak.close();
      }

      return Collections.emptySet();
    }

    boolean interrupted = false;
    while (workerThread.isAlive()) {
      workerThread.interrupt();
      try {
        workerThread.join(100);
      } catch (InterruptedException ignored) {
        interrupted = true;
      }
    }

    if (interrupted) {
      Thread.currentThread().interrupt();
    }

    if (leak != null) {
      leak.close();
    }
    return worker.unprocessedTimeouts();
  }

  @Override
  public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit, String argv) {
    if (task == null) {
      throw new NullPointerException("task");
    }
    if (unit == null) {
      throw new NullPointerException("unit");
    }
    start();

    // 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;
    HashedWheelTimeout timeout = new HashedWheelTimeout(this, task, deadline, argv);
    timeouts.add(timeout);
    return timeout;
  }

  private final class Worker implements Runnable {
    private final Set<Timeout> unprocessedTimeouts = new HashSet<Timeout>();

    private long tick;

    @Override
    public void run() {
      // 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().
      startTimeInitialized.countDown();

      do {
        final long deadline = waitForNextTick();
        if (deadline > 0) {
          int idx = (int) (tick & mask);
          processCancelledTasks();
          HashedWheelBucket bucket =
              wheel[idx];
          transferTimeoutsToBuckets();
          bucket.expireTimeouts(deadline);
          tick++;
        }
      } while (WORKER_STATE_UPDATER.get(TimerWheel.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();
    }

    private void transferTimeoutsToBuckets() {
      // 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 processed
          break;
        }
        if (timeout.state() == HashedWheelTimeout.ST_CANCELLED) {
          // Was cancelled in the meantime.
          continue;
        }

        long calculated = timeout.deadline / tickDuration;
        timeout.remainingRounds = (calculated - tick) / wheel.length;

        final long ticks = Math.max(calculated, tick); // Ensure we don't schedule for past.
        int stopIndex = (int) (ticks & mask);

        HashedWheelBucket bucket = wheel[stopIndex];
        bucket.addTimeout(timeout);
      }
    }

    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);
          }
        }
      }
    }

    /**
     * calculate goal nanoTime from startTime and current tick number,
     * then wait until that goal has been reached.
     * @return Long.MIN_VALUE if received a shutdown request,
     * current time otherwise (with Long.MIN_VALUE changed by +1)
     */
    private long waitForNextTick() {
      long deadline = tickDuration * (tick + 1);

      for (;;) {
        final long 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/356
        if (PlatformDependent.isWindows()) {
          sleepTimeMs = sleepTimeMs / 10 * 10;
        }

        try {
          Thread.sleep(sleepTimeMs);
        } catch (InterruptedException ignored) {
          if (WORKER_STATE_UPDATER.get(TimerWheel.this) == WORKER_STATE_SHUTDOWN) {
            return Long.MIN_VALUE;
          }
        }
      }
    }

    public Set<Timeout> unprocessedTimeouts() {
      return Collections.unmodifiableSet(unprocessedTimeouts);
    }
  }

  private static final class HashedWheelTimeout implements Timeout {

    private static final int ST_INIT = 0;
    private static final int ST_CANCELLED = 1;
    private static final int ST_EXPIRED = 2;
    private static final AtomicIntegerFieldUpdater<HashedWheelTimeout> STATE_UPDATER;

    static {
      AtomicIntegerFieldUpdater<HashedWheelTimeout> updater =
          PlatformDependent.newAtomicIntegerFieldUpdater(HashedWheelTimeout.class, "state");
      if (updater == null) {
        updater = AtomicIntegerFieldUpdater.newUpdater(HashedWheelTimeout.class, "state");
      }
      STATE_UPDATER = updater;
    }

    private final TimerWheel timer;
    private final TimerTask task;
    private final long deadline;

    @SuppressWarnings({"unused", "FieldMayBeFinal", "RedundantFieldInitialization" })
    private volatile int state = ST_INIT;

    // remainingRounds will be calculated and set by Worker.transferTimeoutsToBuckets() before the
    // HashedWheelTimeout will be added to the correct HashedWheelBucket.
    long remainingRounds;
    String argv;

    // This will be used to chain timeouts in HashedWheelTimerBucket via a double-linked-list.
    // As only the workerThread will act on it there is no need for synchronization / volatile.
    HashedWheelTimeout next;
    HashedWheelTimeout prev;

    // The bucket to which the timeout was added
    HashedWheelBucket bucket;

    HashedWheelTimeout(TimerWheel timer, TimerTask task, long deadline, String argv) {
      this.timer = timer;
      this.task = task;
      this.deadline = deadline;
      this.argv = argv;

    }

    @Override
    public Timer timer() {
      return timer;
    }

    @Override
    public TimerTask task() {
      return task;
    }

    @Override
    public boolean cancel() {
      // only update the state it will be removed from HashedWheelBucket on next tick.
      if (!compareAndSetState(ST_INIT, ST_CANCELLED)) {
        return false;
      }
      // 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);
      return true;
    }

    void remove() {
      HashedWheelBucket bucket = this.bucket;
      if (bucket != null) {
        bucket.remove(this);
      }
    }

    public boolean compareAndSetState(int expected, int state) {
      return STATE_UPDATER.compareAndSet(this, expected, state);
    }

    public int state() {
      return state;
    }

    @Override
    public boolean isCancelled() {
      return state() == ST_CANCELLED;
    }

    @Override
    public boolean isExpired() {
      return state() == ST_EXPIRED;
    }

    public void expire() {
      if (!compareAndSetState(ST_INIT, ST_EXPIRED)) {
        return;
      }

      try {
        task.run(this, argv);
      } catch (Throwable t) {
        if (logger.isWarnEnabled()) {
          logger.warn("An exception was thrown by " + TimerTask.class.getSimpleName() + '.', t);
        }
      }
    }

    @Override
    public String toString() {
      final long currentTime = System.nanoTime();
      long remaining = deadline - currentTime + timer.startTime;

      StringBuilder buf = new StringBuilder(192)
          .append(StringUtil.simpleClassName(this))
          .append('(')
          .append("deadline: ");
      if (remaining > 0) {
        buf.append(remaining)
            .append(" ns later");
      } else if (remaining < 0) {
        buf.append(-remaining)
            .append(" ns ago");
      } else {
        buf.append("now");
      }

      if (isCancelled()) {
        buf.append(", cancelled");
      }

      return buf.append(", task: ")
          .append(task())
          .append(')')
          .toString();
    }
  }

  /**
   * Bucket that stores HashedWheelTimeouts. These are stored in a linked-list like datastructure to allow easy
   * removal of HashedWheelTimeouts in the middle. Also the HashedWheelTimeout act as nodes themself and so no
   * extra object creation is needed.
   */
  private static final class HashedWheelBucket {
    // Used for the linked-list datastructure
    private HashedWheelTimeout head;
    private HashedWheelTimeout tail;

    /**
     * Add {@link HashedWheelTimeout} to this bucket.
     */
    public void addTimeout(HashedWheelTimeout timeout) {
      assert timeout.bucket == null;
      timeout.bucket = this;
      if (head == null) {
        head = tail = timeout;
      } else {
        tail.next = timeout;
        timeout.prev = tail;
        tail = timeout;
      }
    }

    /**
     * Expire all {@link HashedWheelTimeout}s for the given {@code deadline}.
     */
    public void expireTimeouts(long deadline) {
      HashedWheelTimeout timeout = head;

      // process all timeouts
      while (timeout != null) {
        boolean remove = false;
        if (timeout.remainingRounds <= 0) {
          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));
          }
          remove = true;
        } else if (timeout.isCancelled()) {
          remove = true;
        } else {
          timeout.remainingRounds --;
        }
        // store reference to next as we may null out timeout.next in the remove block.
        HashedWheelTimeout next = timeout.next;
        if (remove) {
          remove(timeout);
        }
        timeout = next;
      }
    }

    public void remove(HashedWheelTimeout timeout) {
      HashedWheelTimeout next = timeout.next;
      // remove timeout that was either processed or cancelled by updating the linked-list
      if (timeout.prev != null) {
        timeout.prev.next = next;
      }
      if (timeout.next != null) {
        timeout.next.prev = timeout.prev;
      }

      if (timeout == head) {
        // if timeout is also the tail we need to adjust the entry too
        if (timeout == tail) {
          tail = null;
          head = null;
        } else {
          head = next;
        }
      } else if (timeout == tail) {
        // if the timeout is the tail modify the tail to be the prev node.
        tail = timeout.prev;
      }
      // null out prev, next and bucket to allow for GC.
      timeout.prev = null;
      timeout.next = null;
      timeout.bucket = null;
    }

    /**
     * Clear this bucket and return all not expired / cancelled {@link Timeout}s.
     */
    public void clearTimeouts(Set<Timeout> set) {
      for (;;) {
        HashedWheelTimeout timeout = pollTimeout();
        if (timeout == null) {
          return;
        }
        if (timeout.isExpired() || timeout.isCancelled()) {
          continue;
        }
        set.add(timeout);
      }
    }

    private HashedWheelTimeout pollTimeout() {
      HashedWheelTimeout head = this.head;
      if (head == null) {
        return null;
      }
      HashedWheelTimeout next = head.next;
      if (next == null) {
        tail = this.head = null;
      } else {
        this.head = next;
        next.prev = null;
      }

      // null out prev and next to allow for GC.
      head.next = null;
      head.prev = null;
      head.bucket = null;
      return head;
    }
  }
}

编写测试类Main.java

package com.tanghuachun.timer;
import java.util.concurrent.TimeUnit;

/**
 * Created by darren on 2016/11/17.
 */
public class Main implements TimerTask{
  final static Timer timer = new TimerWheel();

  public static void main(String[] args) {
    TimerTask timerTask = new Main();
    for (int i = 0; i < 10; i++) {
      timer.newTimeout(timerTask, 5, TimeUnit.SECONDS, "" + i );
    }
  }
  @Override
  public void run(Timeout timeout, String argv) throws Exception {
    System.out.println("timeout, argv = " + argv );
  }
}

然后就可以看到运行结果啦。

工程代码下载(以maven的方式导入)。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • java算法之二分查找法的实例详解

    java算法之二分查找法的实例详解 原理 假定查找范围为一个有序数组(如升序排列),要从中查找某一元素,如果该元素在此数组中,则返回其索引,否则返回-1.通过数组长度可取出中间位置元素的索引,将其值与目标值比较,如果中间位置元素值大于目标值,则在左部分进行查找,如果中间位置值小于目标值,则在右部分进行查找,如此循环,直到结束.二分查找算法之所以快是因为它没有遍历数组的每个元素,而仅仅是查找部分元素就能找到目标或确定其不存在,当然前提是查找范围为有序数组. Java的简单实现 package me

  • Java中数字黑洞实现代码

    给定任一个各位数字不完全相同的4位正整数,如果我们先把4个数字按非递增排序,再按非递减排序,然后用第1个数字减第2个数字,将得到一个新的数字.一直重复这样做,我们很快会停在有"数字黑洞"之称的6174,这个神奇的数字也叫Kaprekar常数. 例,我们从6767开始,将得到 7766 - 6677 = 1089 9810 - 0189 = 9621 9621 - 1269 = 8352 8532 - 2358 = 6174 7641 - 1467 = 6174 现给定任意4位正整数,请

  • 基于java实现的ECC加密算法示例

    本文实例讲述了基于java实现的ECC加密算法.分享给大家供大家参考,具体如下: ECC ECC-Elliptic Curves Cryptography,椭圆曲线密码编码学,是目前已知的公钥体制中,对每比特所提供加密强度最高的一种体制.在软件注册保护方面起到很大的作用,一般的序列号通常由该算法产生. 当我开始整理<Java加密技术(二)>的时候,我就已经在开始研究ECC了,但是关于Java实现ECC算法的资料实在是太少了,无论是国内还是国外的 资料,无论是官方还是非官方的解释,最终只有一种答

  • 使用递归算法结合数据库解析成Java树形结构的代码解析

    1.准备表结构及对应的表数据 a.表结构: create table TB_TREE ( CID NUMBER not null, CNAME VARCHAR2(50), PID NUMBER //父节点 ) b.表数据: insert into tb_tree (CID, CNAME, PID) values (1, '中国', 0); insert into tb_tree (CID, CNAME, PID) values (2, '北京市', 1); insert into tb_tree

  • 浅谈java实现背包算法(0-1背包问题)

    0-1背包的问题 背包问题(Knapsack problem)是一种组合优化的NP完全问题.问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高.问题的名称来源于如何选择最合适的物品放置于给定背包中. 这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放. 用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值.则其状态转移方程便是: f[i][v]=max{ f[i-1][v], f

  • Java简单实现约瑟夫环算法示例

    本文实例讲述了Java简单实现约瑟夫环算法.分享给大家供大家参考,具体如下: 1.算法背景: 罗马人攻占了乔塔帕特,41人藏在一个山洞中躲过了这场浩劫.这41个人中,包括历史学家josephus和他的一个朋友.剩余的39个人为了表示不向罗马人屈服,决定集体自杀.大家决定了一个自杀方案,所有这41人围城一个圆圈,由第一个人开始顺时针报数,没报数为3的人就立刻自杀,然后由下一个人重新开始报数 仍然是每报数为3的人就立刻自杀,......,知道所有人都自杀死亡为止. 约瑟夫和他的朋友并不想自杀,于是约

  • Java时间轮算法的实现代码示例

    考虑这样一个场景,现在有5000个任务,要让这5000个任务每隔5分中触发某个操作,怎么去实现这个需求.大部分人首先想到的是使用定时器,但是5000个任务,你就要用5000个定时器,一个定时器就是一个线程,你懂了吧,这种方法肯定是不行的. 针对这个场景,催生了时间轮算法,时间轮到底是什么?我一贯的风格,自行谷歌去.大发慈悲,发个时间轮介绍你们看看,看文字和图就好了,代码不要看了,那个文章里的代码运行不起来,时间轮介绍. 看好了介绍,我们就开始动手吧. 开发环境:idea + jdk1.8 + m

  • php计数排序算法的实现代码(附四个实例代码)

    计数排序只适合使用在键的变化不大于元素总数的情况下.它通常用作另一种排序算法(基数排序)的子程序,这样可以有效地处理更大的键. 总之,计数排序是一种稳定的线性时间排序算法.计数排序使用一个额外的数组C ,其中第i个元素是待排序数组 A中值等于 i的元素的个数.然后根据数组C 来将A中的元素排到正确的位置. 通常计数排序算法的实现步骤思路是: 1.找出待排序的数组中最大和最小的元素: 2.统计数组中每个值为i的元素出现的次数,存入数组C的第i项: 3.对所有的计数累加(从C中的第一个元素开始,每一

  • java简单实现八叉树图像处理代码示例

    一晃工作有段时间了,第一次写博客,有点不知道怎么写,大家将就着看吧,说的有什么不正确的也请大家指正. 最近工作中用到了一个图像压缩的功能.找了一些工具,没有太好的选择.最后选了一个叫jdeli的,奈何效率又成了问题.我迫于无奈就只能研究了下它的源码,却发现自己对它的一个减色量化算法起了兴趣,可是尴尬的自己完全不明白它写的什么,就起了一个自己实现一个量化颜色算法的念头. 自己找了一些资料,找到三个比较常用的颜色处理算法: 流行色算法: 具体的算法就是,先对一个图像的所有颜色出现的次数进行统计,选举

  • Java多线程阻塞与唤醒代码示例

    java线程的阻塞及唤醒 1. sleep() 方法: sleep(-毫秒),指定以毫秒为单位的时间,使线程在该时间内进入线程阻塞状态,期间得不到cpu的时间片,等到时间过去了,线程重新进入可执行状态.(暂停线程,不会释放锁) //测试sleep()方法 class Thread7 implements Runnable{ @Override public void run() { for(int i=0;i<50;i++){ System.out.println(Thread.currentT

  • 浅谈Java多线程的优点及代码示例

    尽管面临很多挑战,多线程有一些优点使得它一直被使用.这些优点是: 资源利用率更好 程序设计在某些情况下更简单 程序响应更快 资源利用率更好 想象一下,一个应用程序需要从本地文件系统中读取和处理文件的情景.比方说,从磁盘读取一个文件需要5秒,处理一个文件需要2秒.处理两个文件则需要: 5秒读取文件A 2秒处理文件A 5秒读取文件B 2秒处理文件B --------------------- 总共需要14秒 从磁盘中读取文件的时候,大部分的CPU时间用于等待磁盘去读取数据.在这段时间里,CPU非常的

  • python常用排序算法的实现代码

    这篇文章主要介绍了python常用排序算法的实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 排序是计算机语言需要实现的基本算法之一,有序的数据结构会带来效率上的极大提升. 1.插入排序 插入排序默认当前被插入的序列是有序的,新元素插入到应该插入的位置,使得新序列仍然有序. def insertion_sort(old_list): n=len(old_list) k=0 for i in range(1,n): temp=old_lis

  • Java语言实现反转链表代码示例

    问题描述 定义一个函数,输入一个链表的头结点,反转该链表并输出反转后的链表的头结点.链表结点如下: public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } 思路1: 要想反转链表,对于结点i,我们要把它的next指向它的前趋,因此我们需要保存前趋结点,同时,如果我们已经把i的next重新赋值,会无法找到i的后继,因此,在重新赋值之前,我们要保存i的后继. 代码:

  • Java创建与结束线程代码示例

    本文讲述了在Java中如何创建和结束线程的最基本方法,只针对于Java初学者.一些高级知识如线程同步.调度.线程池等内容将会在后续章节中逐步深入. 创建线程 创建普通线程有两种方式,继承Thread类或实现Runnable接口.示例如下. 方法1:继承Thread类 创建方法示例: public class MyThread1 extends Thread { @Override public void run() { //TODO Auto-generated method stub supe

  • php 二维数组快速排序算法的实现代码

    php 二维数组快速排序算法的实现代码 二维数组排序算法与一维数组排序算法基本理论都是一样,都是通过比较把小的值放在左变的数组里,大的值放在右边的数组里在分别递归. 实例代码: <?php class Bubble { private function __construct() { } private static function sortt($data) { if (count ( $data ) <= 1) { return $data; } $tem = $data [0]['sco

  • Java 时间转换的实例代码

    Java 时间转换的实例代码 import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; /** * Created by Edward on 2016/6/30. */ public class TimeUtil { /** * 将 1467341232351 转换为 指定格式 "yyyy-MM-dd HH:mm:ss.

随机推荐