public int read(int timeout) throws IOException {
    if ((reader == null) || !reader.isAlive()) reader = new Reader();

    try {
      reader.join(timeout);
    } catch (InterruptedException e) {
      throw new IOException("read was interrupted", e);
    }

    handleException();
    if (read == null) throw new TimeoutException();
    return read;
  }
  public void _testGetPut() throws Exception {

    List<NodeData> nodes = prepare();

    Set<Reader> readers = new HashSet<Reader>();
    Set<Writer> writers = new HashSet<Writer>();
    try {
      // create readers
      for (int t = 1; t <= 10; t++) {
        NodeData[] ns = new NodeData[nodes.size()];
        nodes.toArray(ns);
        Reader r = new Reader(ns, "reader #" + t);
        readers.add(r);
        r.start();
      }

      // create writers
      for (int t = 1; t <= 10; t++) {
        NodeData[] ns = new NodeData[nodes.size()];
        nodes.toArray(ns);
        Writer w = new Writer(ns, "writer #" + t, 250);
        writers.add(w);
        w.start();
      }

      Thread.sleep(5 * 60 * 1000);
    } finally {
      // join

      for (Writer w : writers) {
        w.cancel();
        w.join();
      }

      for (Reader r : readers) {
        r.cancel();
        r.join();
      }

      // debug result
      for (Reader r : readers) { // cache.getSize()
        log.info(r.getName() + " " + (r.itemsProcessed));
      }

      for (Writer w : writers) {
        log.info(w.getName() + " " + (w.itemsProcessed));
      }
    }
  }
  public void _testGet() throws Exception {

    List<NodeData> nodes = prepare();

    Set<Reader> readers = new HashSet<Reader>();
    long start = System.currentTimeMillis();
    try {
      // create readers
      for (int t = 1; t <= 200; t++) {
        NodeData[] ns = new NodeData[nodes.size()];
        nodes.toArray(ns);
        Reader r = new Reader(ns, "reader #" + t);
        readers.add(r);
        r.start();
      }
      log.info("Started");
      Thread.sleep(30 * 1000);
      log.info("Done");
    } finally {
      // join
      for (Reader r : readers) {
        r.cancel();
        r.join();
      }

      // debug result
      long totalRead = 0;
      for (Reader r : readers) {
        totalRead += r.itemsProcessed;
        // log.info(r.getName() + " " + (r.itemsProcessed));
      }

      long time = System.currentTimeMillis() - start;
      double speed = totalRead * 1d / time;
      log.info(
          "Total read "
              + totalRead
              + ", speed "
              + speed
              + "read/sec., time "
              + (time / 1000d)
              + "sec");
    }
  }
  public void _testGetPutRemove() throws Exception {

    List<NodeData> nodes = prepare();

    Set<Reader> readers = new HashSet<Reader>();
    Set<Writer> writers = new HashSet<Writer>();
    Set<Remover> removers = new HashSet<Remover>();
    long start = System.currentTimeMillis();
    try {
      // create readers
      for (int t = 1; t <= 100; t++) {
        NodeData[] ns = new NodeData[nodes.size()];
        nodes.toArray(ns);
        Reader r = new Reader(ns, "reader #" + t);
        readers.add(r);
        r.start();
      }

      // create writers
      for (int t = 1; t <= 5; t++) {
        NodeData[] ns = new NodeData[nodes.size()];
        nodes.toArray(ns);
        Writer w = new Writer(ns, "writer #" + t, 1000);
        writers.add(w);
        w.start();
      }

      // create removers
      for (int t = 1; t <= 5; t++) {
        NodeData[] ns = new NodeData[nodes.size()];
        nodes.toArray(ns);
        Remover r = new Remover(ns, "remover #" + t, 1000);
        removers.add(r);
        r.start();
      }

      log.info("Wait....");

      // Thread.sleep(50400 * 1000); // 50400sec = 14h
      Thread.sleep(20 * 1000); // 20sec.

      log.info("Stopping");
    } finally {
      // join
      for (Remover r : removers) {
        r.cancel();
        r.join();
      }

      for (Writer w : writers) {
        w.cancel();
        w.join();
      }

      for (Reader r : readers) {
        r.cancel();
        r.join();
      }

      // debug result
      long stop = System.currentTimeMillis() - start;
      long totalRead = 0;
      for (Reader r : readers) {
        totalRead += r.itemsProcessed;
        // log.info(r.getName() + " " + (r.itemsProcessed));
      }

      for (Writer w : writers) {
        totalRead += w.itemsProcessed;
        // log.info(w.getName() + " " + (w.itemsProcessed));
      }

      for (Remover r : removers) {
        totalRead += r.itemsProcessed;
        // log.info(r.getName() + " " + (r.itemsProcessed));
      }

      double speed = totalRead * 1d / stop;
      log.info(
          "Total accessed "
              + totalRead
              + ", speed "
              + speed
              + " oper/sec., time "
              + (stop / 1000d)
              + "sec");
    }
  }