diff --git a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java
index 0efbfd494a406ee0f05338b420251467ecdc3e56..f77f954a135ef684652f0aed069680080506c763 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java
@@ -328,7 +328,7 @@ public class I2PSnarkUtil {
             return rv;
         } catch (I2PException ie) {
             _banlist.add(dest);
-            _context.simpleScheduler().addEvent(new Unbanlist(dest), 10*60*1000);
+            _context.simpleTimer2().addEvent(new Unbanlist(dest), 10*60*1000);
             IOException ioe = new IOException("Unable to reach the peer " + peer);
             ioe.initCause(ie);
             throw ioe;
diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
index 6f46cd9de196f283320b508b3b635f6e161f921b..e8931328c4986e76a7314a7cbbfb1f86acc4a560 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
@@ -233,7 +233,7 @@ public class SnarkManager implements CompleteListener {
         // only if default instance
         if ("i2psnark".equals(_contextName))
             // delay until UpdateManager is there
-            _context.simpleScheduler().addEvent(new Register(), 4*60*1000);
+            _context.simpleTimer2().addEvent(new Register(), 4*60*1000);
         // Not required, Jetty has a shutdown hook
         //_context.addShutdownTask(new SnarkManagerShutdown());
         _idleChecker = new IdleChecker(this, _peerCoordinatorSet);
@@ -2272,7 +2272,7 @@ public class SnarkManager implements CompleteListener {
                     dht.stop();
                 // Schedule this even for final shutdown, as there's a chance
                 // that it's just this webapp that is stopping.
-                _context.simpleScheduler().addEvent(new Disconnector(), 60*1000);
+                _context.simpleTimer2().addEvent(new Disconnector(), 60*1000);
                 addMessage(_("Closing I2P tunnel after notifying trackers."));
                 if (finalShutdown) {
                     try { Thread.sleep(5*1000); } catch (InterruptedException ie) {}
diff --git a/apps/routerconsole/java/src/net/i2p/router/news/NewsEntry.java b/apps/routerconsole/java/src/net/i2p/router/news/NewsEntry.java
index de6521b8099c32fd3fac69bce3b4377127c18e2d..39709144c1824ae3369d3c045505fd9d1fe30928 100644
--- a/apps/routerconsole/java/src/net/i2p/router/news/NewsEntry.java
+++ b/apps/routerconsole/java/src/net/i2p/router/news/NewsEntry.java
@@ -17,6 +17,7 @@ public class NewsEntry implements Comparable<NewsEntry> {
     public String authorName;  // subnode of author
 
     /** reverse, newest first */
+    @Override
     public int compareTo(NewsEntry e) {
         if (updated > e.updated)
             return -1;
@@ -24,4 +25,22 @@ public class NewsEntry implements Comparable<NewsEntry> {
             return 1;
         return 0;
     }
+    
+    @Override
+    public boolean equals(Object o) {
+        if(o == null) {
+        	return false;
+        }
+        if(!(o instanceof NewsEntry)) {
+        	return false;
+        }
+    	NewsEntry e = (NewsEntry) o;
+    	
+    	return this.compareTo(e) == 0;
+    }
+    
+    @Override
+    public int hashCode() {
+    	return (int) updated;
+    }
 }
diff --git a/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java b/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java
index 3d2919f396e8cbdfa16eba8164a824c637d63c44..4f777a5fb210e39a535a3b6a36650e1adfb5e3eb 100644
--- a/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java
+++ b/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java
@@ -193,7 +193,7 @@ public class ConsoleUpdateManager implements UpdateManager, RouterApp {
         // handled inside P.U.H. for now
         //register((Updater)puh, PLUGIN, FILE, 0);
         new NewsTimerTask(_context, this);
-        _context.simpleScheduler().addPeriodicEvent(new TaskCleaner(), TASK_CLEANER_TIME);
+        _context.simpleTimer2().addPeriodicEvent(new TaskCleaner(), TASK_CLEANER_TIME);
         changeState(RUNNING);
         if (_cmgr != null)
             _cmgr.register(this);
@@ -1397,7 +1397,7 @@ public class ConsoleUpdateManager implements UpdateManager, RouterApp {
 
     private void finishStatus(String msg) {
         updateStatus(msg);
-        _context.simpleScheduler().addEvent(new StatusCleaner(msg), STATUS_CLEAN_TIME);
+        _context.simpleTimer2().addEvent(new StatusCleaner(msg), STATUS_CLEAN_TIME);
     }
 
     private class StatusCleaner implements SimpleTimer.TimedEvent {
diff --git a/apps/routerconsole/java/src/net/i2p/router/update/NewsTimerTask.java b/apps/routerconsole/java/src/net/i2p/router/update/NewsTimerTask.java
index 3dc93c71b4af99259fab5dc2bab03b454ab6f3d3..26116a7ec94c4e310b07991c169d0feb32f45d6c 100644
--- a/apps/routerconsole/java/src/net/i2p/router/update/NewsTimerTask.java
+++ b/apps/routerconsole/java/src/net/i2p/router/update/NewsTimerTask.java
@@ -38,7 +38,7 @@ class NewsTimerTask implements SimpleTimer.TimedEvent {
         delay += _context.random().nextLong(INITIAL_DELAY);
         if (_log.shouldLog(Log.INFO))
             _log.info("Scheduling first news check in " + DataHelper.formatDuration(delay));
-        ctx.simpleScheduler().addPeriodicEvent(this, delay, RUN_DELAY);
+        ctx.simpleTimer2().addPeriodicEvent(this, delay, RUN_DELAY);
         // UpdateManager calls NewsFetcher to check the existing news at startup
     }
 
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/impl/ConnThrottler.java b/apps/streaming/java/src/net/i2p/client/streaming/impl/ConnThrottler.java
index 14add7b3f53fe75751900b48713150e0da4f648e..51941d219aab8b0469e344fb757c6c8aaf6af35b 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/impl/ConnThrottler.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/impl/ConnThrottler.java
@@ -5,8 +5,8 @@ import java.util.concurrent.atomic.AtomicInteger;
 import net.i2p.data.Hash;
 import net.i2p.util.ObjectCounter;
 import net.i2p.util.RandomSource;
-import net.i2p.util.SimpleScheduler;
 import net.i2p.util.SimpleTimer;
+import net.i2p.util.SimpleTimer2;
 
 /**
  * Count how often we have received an incoming connection
@@ -33,7 +33,7 @@ class ConnThrottler {
         // shorten the initial period by a random amount
         // to prevent correlation across destinations
         // and identification of router startup time
-        SimpleScheduler.getInstance().addPeriodicEvent(new Cleaner(),
+        SimpleTimer2.getInstance().addPeriodicEvent(new Cleaner(),
                                                        (period / 2) + RandomSource.getInstance().nextLong(period / 2),
                                                        period);
     }
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/impl/Connection.java b/apps/streaming/java/src/net/i2p/client/streaming/impl/Connection.java
index 72f1022335f056b3695f5fa3957d70052ec49005..768b055cbb07c2b97e56f497ea1494f8c9b66308 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/impl/Connection.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/impl/Connection.java
@@ -781,7 +781,7 @@ class Connection {
     private boolean scheduleDisconnectEvent() {
         if (!_disconnectScheduledOn.compareAndSet(0, _context.clock().now()))
             return false;
-        _context.simpleScheduler().addEvent(new DisconnectEvent(), DISCONNECT_TIMEOUT);
+        _context.simpleTimer2().addEvent(new DisconnectEvent(), DISCONNECT_TIMEOUT);
         return true;
     }
 
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/impl/ConnectionPacketHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/impl/ConnectionPacketHandler.java
index 0595c8471155a5977eada68d841a5fe4f3a0847b..26def9b07cd60681f75b28e52926524703b2da84 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/impl/ConnectionPacketHandler.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/impl/ConnectionPacketHandler.java
@@ -213,7 +213,7 @@ class ConnectionPacketHandler {
                     final long delay = nextSendTime - now;
                     if (_log.shouldLog(Log.DEBUG)) 
                         _log.debug("scheduling ack in "+delay);
-                    _context.simpleScheduler().addEvent(new AckDup(con), delay);
+                    _context.simpleTimer2().addEvent(new AckDup(con), delay);
                 }
 
             } else {
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/impl/SchedulerImpl.java b/apps/streaming/java/src/net/i2p/client/streaming/impl/SchedulerImpl.java
index 7a5b63574bd305f23067518a515425c7c22094d4..41085a30d25760ea56a7f4f4c54774571d9cd223 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/impl/SchedulerImpl.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/impl/SchedulerImpl.java
@@ -16,7 +16,7 @@ abstract class SchedulerImpl implements TaskScheduler {
     }
     
     protected void reschedule(long msToWait, Connection con) {
-        _context.simpleScheduler().addEvent(con.getConnectionEvent(), msToWait);
+        _context.simpleTimer2().addEvent(con.getConnectionEvent(), msToWait);
     }
 
     @Override
diff --git a/apps/systray/java/src/net/i2p/apps/systray/SysTray.java b/apps/systray/java/src/net/i2p/apps/systray/SysTray.java
index 21f6f45f7989a37f560dd2a9ed3bdf9392e2f78f..8d030d09171d48309f955332140b7dbc7cba99ce 100644
--- a/apps/systray/java/src/net/i2p/apps/systray/SysTray.java
+++ b/apps/systray/java/src/net/i2p/apps/systray/SysTray.java
@@ -13,10 +13,9 @@ import java.awt.Frame;
 import java.io.File;
 
 import net.i2p.I2PAppContext;
-import net.i2p.util.SimpleScheduler;
 import net.i2p.util.SimpleTimer;
+import net.i2p.util.SimpleTimer2;
 import net.i2p.util.SystemVersion;
-
 import snoozesoft.systray4j.SysTrayMenu;
 import snoozesoft.systray4j.SysTrayMenuEvent;
 import snoozesoft.systray4j.SysTrayMenuIcon;
@@ -66,7 +65,7 @@ public class SysTray implements SysTrayMenuListener {
     private SysTray() {
         _sysTrayMenuIcon.addSysTrayMenuListener(this);
         createSysTrayMenu();
-        SimpleScheduler.getInstance().addPeriodicEvent(new RefreshDisplayEvent(), REFRESH_DISPLAY_FREQUENCY);
+        SimpleTimer2.getInstance().addPeriodicEvent(new RefreshDisplayEvent(), REFRESH_DISPLAY_FREQUENCY);
     }
     
     private static final long REFRESH_DISPLAY_FREQUENCY = 30*1000;
diff --git a/core/java/src/net/i2p/I2PAppContext.java b/core/java/src/net/i2p/I2PAppContext.java
index 71ae745076019052c6da5394bd7caf16c9118b51..5a031a823fa51433ed1bf3f28cec7b426cc00b25 100644
--- a/core/java/src/net/i2p/I2PAppContext.java
+++ b/core/java/src/net/i2p/I2PAppContext.java
@@ -939,6 +939,7 @@ public class I2PAppContext {
     /**
      * Use instead of SimpleScheduler.getInstance()
      * @since 0.9 to replace static instance in the class
+     * @deprecated in 0.9.19
      */
     public SimpleScheduler simpleScheduler() {
         if (!_simpleSchedulerInitialized)
@@ -946,6 +947,9 @@ public class I2PAppContext {
         return _simpleScheduler;
     }
 
+    /**
+     * @deprecated in 0.9.19
+     */
     private void initializeSimpleScheduler() {
         synchronized (_lock18) {
             if (_simpleScheduler == null)
diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java
index 0c89835282ca4563716c168e74ceabc45dae8b70..ce5f44ef4da3249084fd642470da72782cb8c041 100644
--- a/core/java/src/net/i2p/client/I2PSessionImpl.java
+++ b/core/java/src/net/i2p/client/I2PSessionImpl.java
@@ -1353,7 +1353,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
         boolean close = Boolean.parseBoolean(_options.getProperty("i2cp.closeOnIdle"));
         if (reduce || close) {
             updateActivity();
-            _context.simpleScheduler().addEvent(new SessionIdleTimer(_context, this, reduce, close), SessionIdleTimer.MINIMUM_TIME);
+            _context.simpleTimer2().addEvent(new SessionIdleTimer(_context, this, reduce, close), SessionIdleTimer.MINIMUM_TIME);
         }
     }
 
diff --git a/core/java/src/net/i2p/client/SessionIdleTimer.java b/core/java/src/net/i2p/client/SessionIdleTimer.java
index d01273f930a32bb9872e2a8b845077a9fed81ffe..444f73167fffac2818e6bfc4e1c943a29242990d 100644
--- a/core/java/src/net/i2p/client/SessionIdleTimer.java
+++ b/core/java/src/net/i2p/client/SessionIdleTimer.java
@@ -118,6 +118,6 @@ class SessionIdleTimer implements SimpleTimer.TimedEvent {
         } else {
             nextDelay = _minimumTime - (now - lastActivity);
         }
-        _context.simpleScheduler().addEvent(this, nextDelay);
+        _context.simpleTimer2().addEvent(this, nextDelay);
     }
 }
diff --git a/core/java/src/net/i2p/util/ByteCache.java b/core/java/src/net/i2p/util/ByteCache.java
index 5f64e9649a4ccebb4c2943161e65f728924e0aa4..dcc69d0c3d8aff1cec55d8353e8e0323eed6207c 100644
--- a/core/java/src/net/i2p/util/ByteCache.java
+++ b/core/java/src/net/i2p/util/ByteCache.java
@@ -123,7 +123,7 @@ public final class ByteCache {
         _maxCached = maxCachedEntries;
         _entrySize = entrySize;
         _lastOverflow = -1;
-        SimpleScheduler.getInstance().addPeriodicEvent(new Cleanup(), CLEANUP_FREQUENCY + (entrySize % 777));   //stagger
+        SimpleTimer2.getInstance().addPeriodicEvent(new Cleanup(), CLEANUP_FREQUENCY + (entrySize % 777));   //stagger
         I2PAppContext.getGlobalContext().statManager().createRateStat("byteCache.memory." + entrySize, "Memory usage (B)", "Router", new long[] { 10*60*1000 });
     }
     
diff --git a/core/java/src/net/i2p/util/SimpleScheduler.java b/core/java/src/net/i2p/util/SimpleScheduler.java
index 9d39a4ec5dcfa371e36c14e467549c9bc8e7d744..1cea34298b089162f5cd080c192204b373ea19ca 100644
--- a/core/java/src/net/i2p/util/SimpleScheduler.java
+++ b/core/java/src/net/i2p/util/SimpleScheduler.java
@@ -30,6 +30,7 @@ public class SimpleScheduler {
 
     /**
      *  If you have a context, use context.simpleScheduler() instead
+     *  @deprecated in 0.9.19
      */
     public static SimpleScheduler getInstance() {
         return I2PAppContext.getGlobalContext().simpleScheduler();
@@ -46,6 +47,7 @@ public class SimpleScheduler {
     /**
      *  To be instantiated by the context.
      *  Others should use context.simpleTimer() instead
+     *  @deprecated in 0.9.19
      */
     public SimpleScheduler(I2PAppContext context) {
         this(context, "SimpleScheduler");
@@ -54,6 +56,7 @@ public class SimpleScheduler {
     /**
      *  To be instantiated by the context.
      *  Others should use context.simpleTimer() instead
+     *  @deprecated in 0.9.19
      */
     private SimpleScheduler(I2PAppContext context, String name) {
         _log = context.logManager().getLog(SimpleScheduler.class);
diff --git a/core/java/src/net/i2p/util/SimpleTimer2.java b/core/java/src/net/i2p/util/SimpleTimer2.java
index 9526ef0693639a7a0111a76612ee2983c2bff8c2..497eed6d4163df3bc6edfd3bcb297e0e6918e91c 100644
--- a/core/java/src/net/i2p/util/SimpleTimer2.java
+++ b/core/java/src/net/i2p/util/SimpleTimer2.java
@@ -124,6 +124,66 @@ public class SimpleTimer2 {
     private ScheduledFuture schedule(TimedEvent t, long timeoutMs) {
         return _executor.schedule(t, timeoutMs, TimeUnit.MILLISECONDS);
     }
+    
+    /**
+     * Queue up the given event to be fired no sooner than timeoutMs from now.
+     *
+     * @param event
+     * @param timeoutMs 
+     */
+    public void addEvent(final SimpleTimer.TimedEvent event, long timeoutMs) {
+        if (event == null)
+            throw new IllegalArgumentException("addEvent null");
+
+        new TimedEvent(SimpleTimer2.getInstance(), timeoutMs) {
+            @Override
+            public void timeReached() {
+                event.timeReached();
+            }
+        };
+    }
+    
+    /**
+     * Schedule periodic event
+     * 
+     * The TimedEvent must not do its own rescheduling.
+     * As all Exceptions are caught in run(), these will not prevent
+     * subsequent executions (unlike SimpleTimer, where the TimedEvent does
+     * its own rescheduling).
+     * 
+     * @param delay run the first iteration of this event after delay ms
+     * @param timeoutMs run subsequent iterations of this event every timeoutMs ms
+     */
+    public void addPeriodicEvent(final SimpleTimer.TimedEvent event, final long timeoutMs) {
+        
+        new PeriodicTimedEvent(SimpleTimer2.getInstance(), timeoutMs) {
+            @Override
+            public void timeReached() {
+                event.timeReached();
+            }
+        };
+    }
+    
+    /**
+     * Schedule periodic event
+     * 
+     * The TimedEvent must not do its own rescheduling.
+     * As all Exceptions are caught in run(), these will not prevent
+     * subsequent executions (unlike SimpleTimer, where the TimedEvent does
+     * its own rescheduling).
+     * 
+     * @param delay run the first iteration of this event after delay ms
+     * @param timeoutMs run subsequent iterations of this event every timeoutMs ms
+     */
+    public void addPeriodicEvent(final SimpleTimer.TimedEvent event, final long delay,  final long timeoutMs) {
+        
+        new PeriodicTimedEvent(SimpleTimer2.getInstance(), delay, timeoutMs) {
+            @Override
+            public void timeReached() {
+                event.timeReached();
+            }
+        };        
+    }
 
     /** 
      * state of a given TimedEvent
@@ -141,6 +201,7 @@ public class SimpleTimer2 {
         CANCELLED
     };
     
+    
     /**
      * Similar to SimpleTimer.TimedEvent but users must extend instead of implement,
      * and all schedule and cancel methods are through this class rather than SimpleTimer2.
@@ -228,7 +289,6 @@ public class SimpleTimer2 {
                 break;
               case SCHEDULED: // nothing
             }
-            
         }
 
         /**
@@ -403,5 +463,36 @@ public class SimpleTimer2 {
             " Completed: " + _executor.getCompletedTaskCount() +
             " Queued: " + _executor.getQueue().size();
     }
+    
+    public static abstract class PeriodicTimedEvent extends TimedEvent {
+        private long _timeoutMs;
+        
+        /**
+         * Schedule periodic event
+         * 
+         * @param timeoutMs run subsequent iterations of this event every timeoutMs ms
+         */
+        public PeriodicTimedEvent(SimpleTimer2 pool, long timeoutMs) {
+            super(pool, timeoutMs);
+            _timeoutMs = timeoutMs;
+        }
+        
+        /**
+         * Schedule periodic event
+         * 
+         * @param delay run the first iteration of this event after delay ms
+         * @param timeoutMs run subsequent iterations of this event every timeoutMs ms
+         */
+        public PeriodicTimedEvent(SimpleTimer2 pool, long delay, long timeoutMs) {
+            super(pool, delay);
+            _timeoutMs = timeoutMs;
+        }
+        
+        @Override
+        public void run() {
+            super.run();
+            schedule(_timeoutMs);
+        }
+    }
 }
 
diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java
index 4916d8d725d08f0f3497eaf0cc3e06540826b9d1..94844a3bf4fc8506d0c8fe0bc67c51d628d56374 100644
--- a/router/java/src/net/i2p/router/Router.java
+++ b/router/java/src/net/i2p/router/Router.java
@@ -576,7 +576,7 @@ public class Router implements RouterClock.ClockShiftListener {
         _context.inNetMessagePool().startup();
         startupQueue();
         //_context.jobQueue().addJob(new CoalesceStatsJob(_context));
-        _context.simpleScheduler().addPeriodicEvent(new CoalesceStatsEvent(_context), COALESCE_TIME);
+        _context.simpleTimer2().addPeriodicEvent(new CoalesceStatsEvent(_context), COALESCE_TIME);
         _context.jobQueue().addJob(new UpdateRoutingKeyModifierJob(_context));
         //_context.adminManager().startup();
         _context.blocklist().startup();
@@ -840,7 +840,7 @@ public class Router implements RouterClock.ClockShiftListener {
             if (blockingRebuild)
                 r.timeReached();
             else
-                _context.simpleScheduler().addEvent(r, 0);
+                _context.simpleTimer2().addEvent(r, 0);
         } catch (DataFormatException dfe) {
             _log.log(Log.CRIT, "Internal error - unable to sign our own address?!", dfe);
         }
@@ -1747,7 +1747,7 @@ public class Router implements RouterClock.ClockShiftListener {
      */
     private void beginMarkingLiveliness() {
         File f = getPingFile();
-        _context.simpleScheduler().addPeriodicEvent(new MarkLiveliness(this, f), 0, LIVELINESS_DELAY - (5*1000));
+        _context.simpleTimer2().addPeriodicEvent(new MarkLiveliness(this, f), 0, LIVELINESS_DELAY - (5*1000));
     }
     
     public static final String PROP_BANDWIDTH_SHARE_PERCENTAGE = "router.sharePercentage";
diff --git a/router/java/src/net/i2p/router/RouterThrottleImpl.java b/router/java/src/net/i2p/router/RouterThrottleImpl.java
index 3bae2fd964aafc03a077d322bfd5bbe834fc72b4..66a31acce26279a7f402137262253d3ee1fa609d 100644
--- a/router/java/src/net/i2p/router/RouterThrottleImpl.java
+++ b/router/java/src/net/i2p/router/RouterThrottleImpl.java
@@ -50,7 +50,7 @@ class RouterThrottleImpl implements RouterThrottle {
         _context = context;
         _log = context.logManager().getLog(RouterThrottleImpl.class);
         setTunnelStatus();
-        _context.simpleScheduler().addEvent(new ResetStatus(), 5*1000 + _context.getProperty(PROP_REJECT_STARTUP_TIME, DEFAULT_REJECT_STARTUP_TIME));
+        _context.simpleTimer2().addEvent(new ResetStatus(), 5*1000 + _context.getProperty(PROP_REJECT_STARTUP_TIME, DEFAULT_REJECT_STARTUP_TIME));
         _context.statManager().createRateStat("router.throttleNetworkCause", "How lagged the jobQueue was when an I2NP was throttled", "Throttle", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
         //_context.statManager().createRateStat("router.throttleNetDbCause", "How lagged the jobQueue was when a networkDb request was throttled", "Throttle", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
         _context.statManager().createRateStat("router.throttleTunnelCause", "How lagged the jobQueue was when a tunnel request was throttled", "Throttle", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
diff --git a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
index fbfa0a560d873698ebf39f8dd3edce36ee6b28f3..a8442d4962bb87472b95363987716faebf9812d8 100644
--- a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
+++ b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
@@ -557,7 +557,7 @@ class ClientConnectionRunner {
                     // theirs is newer
                 } else {
                     // ours is newer, so wait a few secs and retry
-                    _context.simpleScheduler().addEvent(new Rerequest(set, expirationTime, onCreateJob, onFailedJob), 3*1000);
+                    _context.simpleTimer2().addEvent(new Rerequest(set, expirationTime, onCreateJob, onFailedJob), 3*1000);
                 }
                 // fire onCreated?
                 return; // already requesting
diff --git a/router/java/src/net/i2p/router/crypto/TransientSessionKeyManager.java b/router/java/src/net/i2p/router/crypto/TransientSessionKeyManager.java
index 6b4f5e7b13fa439c5f7fe87588dc97d67edb57d4..15f0ec601691dc9d8f537d3f6105d5694839243c 100644
--- a/router/java/src/net/i2p/router/crypto/TransientSessionKeyManager.java
+++ b/router/java/src/net/i2p/router/crypto/TransientSessionKeyManager.java
@@ -183,7 +183,7 @@ public class TransientSessionKeyManager extends SessionKeyManager {
         context.statManager().createRateStat("crypto.sessionTagsExpired", "How many tags/sessions are expired?", "Encryption", new long[] { 10*60*1000, 60*60*1000, 3*60*60*1000 });
         context.statManager().createRateStat("crypto.sessionTagsRemaining", "How many tags/sessions are remaining after a cleanup?", "Encryption", new long[] { 10*60*1000, 60*60*1000, 3*60*60*1000 });
          _alive = true;
-        _context.simpleScheduler().addEvent(new CleanupEvent(), 60*1000);
+        _context.simpleTimer2().addEvent(new CleanupEvent(), 60*1000);
     }
     
     @Override
@@ -205,7 +205,7 @@ public class TransientSessionKeyManager extends SessionKeyManager {
             int expired = aggressiveExpire();
             long expireTime = _context.clock().now() - beforeExpire;
             _context.statManager().addRateData("crypto.sessionTagsExpired", expired, expireTime);
-            _context.simpleScheduler().addEvent(this, 60*1000);
+            _context.simpleTimer2().addEvent(this, 60*1000);
         }
     }
 
diff --git a/router/java/src/net/i2p/router/message/OutboundCache.java b/router/java/src/net/i2p/router/message/OutboundCache.java
index bc3dcd653940161c4650dfb4b24af7309604aed0..6a4856b978c7f44935df250f36b81c8bb81ab55d 100644
--- a/router/java/src/net/i2p/router/message/OutboundCache.java
+++ b/router/java/src/net/i2p/router/message/OutboundCache.java
@@ -101,7 +101,7 @@ public class OutboundCache {
     
     public OutboundCache(RouterContext ctx) {
         _context = ctx;
-        _context.simpleScheduler().addPeriodicEvent(new OCMOSJCacheCleaner(), CLEAN_INTERVAL, CLEAN_INTERVAL);
+        _context.simpleTimer2().addPeriodicEvent(new OCMOSJCacheCleaner(), CLEAN_INTERVAL, CLEAN_INTERVAL);
     }
 
     /**
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodThrottler.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodThrottler.java
index 75a56bc4020642f9b03236af1146c650fd5f719b..caf57e0a350f20bff096d64a293eea6c57f85508 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodThrottler.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodThrottler.java
@@ -2,8 +2,8 @@ package net.i2p.router.networkdb.kademlia;
 
 import net.i2p.data.Hash;
 import net.i2p.util.ObjectCounter;
-import net.i2p.util.SimpleScheduler;
 import net.i2p.util.SimpleTimer;
+import net.i2p.util.SimpleTimer2;
 
 /**
  * Count how often we have recently flooded a key
@@ -18,7 +18,7 @@ class FloodThrottler {
 
     FloodThrottler() {
         this.counter = new ObjectCounter<Hash>();
-        SimpleScheduler.getInstance().addPeriodicEvent(new Cleaner(), CLEAN_TIME);
+        SimpleTimer2.getInstance().addPeriodicEvent(new Cleaner(), CLEAN_TIME);
     }
 
     /** increments before checking */
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/LookupThrottler.java b/router/java/src/net/i2p/router/networkdb/kademlia/LookupThrottler.java
index dc0e84bcba727bf492f7b9111231cee080c659db..28ae14f76361641b3a111a41935a338ae525aa8f 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/LookupThrottler.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/LookupThrottler.java
@@ -3,8 +3,8 @@ package net.i2p.router.networkdb.kademlia;
 import net.i2p.data.Hash;
 import net.i2p.data.TunnelId;
 import net.i2p.util.ObjectCounter;
-import net.i2p.util.SimpleScheduler;
 import net.i2p.util.SimpleTimer;
+import net.i2p.util.SimpleTimer2;
 
 /**
  * Count how often we have recently received a lookup request with
@@ -25,7 +25,7 @@ class LookupThrottler {
 
     LookupThrottler() {
         this.counter = new ObjectCounter<ReplyTunnel>();
-        SimpleScheduler.getInstance().addPeriodicEvent(new Cleaner(), CLEAN_TIME);
+        SimpleTimer2.getInstance().addPeriodicEvent(new Cleaner(), CLEAN_TIME);
     }
 
     /**
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/NegativeLookupCache.java b/router/java/src/net/i2p/router/networkdb/kademlia/NegativeLookupCache.java
index 1784f9434d075a7f892deea38f934a3acb69a109..1915caf4ee56d890e237ef9082061291c0aa23ab 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/NegativeLookupCache.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/NegativeLookupCache.java
@@ -1,12 +1,13 @@
 package net.i2p.router.networkdb.kademlia;
 
 import java.util.Map;
+
 import net.i2p.data.Destination;
 import net.i2p.data.Hash;
 import net.i2p.util.LHMCache;
 import net.i2p.util.ObjectCounter;
-import net.i2p.util.SimpleScheduler;
 import net.i2p.util.SimpleTimer;
+import net.i2p.util.SimpleTimer2;
 
 /**
  * Track lookup fails
@@ -24,7 +25,7 @@ class NegativeLookupCache {
     public NegativeLookupCache() {
         this.counter = new ObjectCounter<Hash>();
         this.badDests = new LHMCache<Hash, Destination>(MAX_BAD_DESTS);
-        SimpleScheduler.getInstance().addPeriodicEvent(new Cleaner(), CLEAN_TIME);
+        SimpleTimer2.getInstance().addPeriodicEvent(new Cleaner(), CLEAN_TIME);
     }
 
     public void lookupFailed(Hash h) {
diff --git a/router/java/src/net/i2p/router/networkdb/reseed/ReseedChecker.java b/router/java/src/net/i2p/router/networkdb/reseed/ReseedChecker.java
index df4f31300c8f0953d4e81effc75f9c85b79f3eb0..ed5955d3d82069ab417e935607524f8a8556226d 100644
--- a/router/java/src/net/i2p/router/networkdb/reseed/ReseedChecker.java
+++ b/router/java/src/net/i2p/router/networkdb/reseed/ReseedChecker.java
@@ -184,7 +184,7 @@ public class ReseedChecker {
      */
     void done() {
         _inProgress.set(false);
-        _context.simpleScheduler().addEvent(new StatusCleaner(_lastStatus, _lastError), STATUS_CLEAN_TIME);
+        _context.simpleTimer2().addEvent(new StatusCleaner(_lastStatus, _lastError), STATUS_CLEAN_TIME);
     }
 
     /**
diff --git a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java
index f31bcce373bf3ce51122433ec8f47a6cb6355fb5..3ac79186658d6868463882cf0a45312124ed5a78 100644
--- a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java
+++ b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java
@@ -278,7 +278,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
     private static final int LOOKUP_TIME = 30*60*1000;
 
     private void startGeoIP() {
-        _context.simpleScheduler().addEvent(new QueueAll(), START_DELAY);
+        _context.simpleTimer2().addEvent(new QueueAll(), START_DELAY);
     }
 
     /**
@@ -296,7 +296,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
                     continue;
                 _geoIP.add(ip);
             }
-            _context.simpleScheduler().addPeriodicEvent(new Lookup(), 5000, LOOKUP_TIME);
+            _context.simpleTimer2().addPeriodicEvent(new Lookup(), 5000, LOOKUP_TIME);
         }
     }
 
@@ -491,7 +491,7 @@ public class CommSystemFacadeImpl extends CommSystemFacade {
 
     /** @since 0.7.12 */
     private void startTimestamper() {
-        _context.simpleScheduler().addPeriodicEvent(new Timestamper(), TIME_START_DELAY,  TIME_REPEAT_DELAY);
+        _context.simpleTimer2().addPeriodicEvent(new Timestamper(), TIME_START_DELAY,  TIME_REPEAT_DELAY);
     }
 
     /**
diff --git a/router/java/src/net/i2p/router/transport/TransportImpl.java b/router/java/src/net/i2p/router/transport/TransportImpl.java
index 1b54d5c707ae3dac376a6cb89afa31a372797f1d..92b9a61d995c86ea0e517ad1e2658577d876e6b6 100644
--- a/router/java/src/net/i2p/router/transport/TransportImpl.java
+++ b/router/java/src/net/i2p/router/transport/TransportImpl.java
@@ -102,7 +102,7 @@ public abstract class TransportImpl implements Transport {
         _unreachableEntries = new HashMap<Hash, Long>(32);
         _wasUnreachableEntries = new HashMap<Hash, Long>(32);
         _localAddresses = new ConcurrentHashSet<InetAddress>(4);
-        _context.simpleScheduler().addPeriodicEvent(new CleanupUnreachable(), 2 * UNREACHABLE_PERIOD, UNREACHABLE_PERIOD / 2);
+        _context.simpleTimer2().addPeriodicEvent(new CleanupUnreachable(), 2 * UNREACHABLE_PERIOD, UNREACHABLE_PERIOD / 2);
     }
 
     /**
diff --git a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
index c44339dfe64a4f6dab92db4971e48dc52be028b0..1702b69c125638612738e2456ffb55227c4ce87a 100644
--- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java
@@ -595,12 +595,7 @@ class EstablishmentManager {
                 }
                 
         if (_outboundStates.size() < getMaxConcurrentEstablish() && !_queuedOutbound.isEmpty()) {
-            // in theory shouldn't need locking, but
-            // getting IllegalStateExceptions on old Java 5,
-            // which hoses this state.
-            synchronized(_queuedOutbound) {
-                locked_admitQueued();
-            }
+            locked_admitQueued();
         }
             //remaining = _queuedOutbound.size();
 
@@ -753,7 +748,7 @@ class EstablishmentManager {
         _transport.send(dsm, peer);
 
         // just do this inline
-        //_context.simpleScheduler().addEvent(new PublishToNewInbound(peer), 0);
+        //_context.simpleTimer2().addEvent(new PublishToNewInbound(peer), 0);
 
             Hash hash = peer.getRemotePeer();
             if ((hash != null) && (!_context.banlist().isBanlisted(hash)) && (!_transport.isUnreachable(hash))) {
diff --git a/router/java/src/net/i2p/router/transport/udp/IPThrottler.java b/router/java/src/net/i2p/router/transport/udp/IPThrottler.java
index 782988ec3e9cb3082168add2e711c4a7b7bcea85..a318c910117c002f8edf5fa6272ae4cbef64c8a6 100644
--- a/router/java/src/net/i2p/router/transport/udp/IPThrottler.java
+++ b/router/java/src/net/i2p/router/transport/udp/IPThrottler.java
@@ -1,8 +1,8 @@
 package net.i2p.router.transport.udp;
 
 import net.i2p.util.ObjectCounter;
-import net.i2p.util.SimpleScheduler;
 import net.i2p.util.SimpleTimer;
+import net.i2p.util.SimpleTimer2;
 import net.i2p.util.SipHash;
 
 /**
@@ -17,7 +17,7 @@ class IPThrottler {
     public IPThrottler(int max, long time) {
         _max = max;
         _counter = new ObjectCounter<Integer>();
-        SimpleScheduler.getInstance().addPeriodicEvent(new Cleaner(), time);
+        SimpleTimer2.getInstance().addPeriodicEvent(new Cleaner(), time);
     }
 
     /**
diff --git a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
index 6a0b41253383f549a9beb8871e3579dd44339b44..9bc2bfd72a29f33f4ccaf9a49e57c901311b34c7 100644
--- a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
@@ -259,9 +259,28 @@ class IntroductionManager {
             stag = String.valueOf(tag);
         }
 
+        @Override
         public int compareTo(Introducer i) {
             return skey.compareTo(i.skey);
         }
+        
+        @Override
+        public boolean equals(Object o) {
+        	if (o == null) {
+        		return false;
+        	}
+        	if (!(o instanceof Introducer)) {
+        		return false;
+        	}
+        	
+        	Introducer i = (Introducer) o;
+        	return this.compareTo(i) == 0;
+        }
+        
+        @Override
+        public int hashCode() {
+        	return skey.hashCode(); 
+        }
     }
 
     /**
diff --git a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
index 3fd6432b44d1aba0912ec675b6d90d119bde11d6..c1078f4e6ac7f61e7c8f774489d87f141125b0d9 100644
--- a/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
+++ b/router/java/src/net/i2p/router/transport/udp/PeerTestManager.java
@@ -207,7 +207,7 @@ class PeerTestManager {
         test.incrementPacketsRelayed();
         sendTestToBob();
         
-        _context.simpleScheduler().addEvent(new ContinueTest(test.getNonce()), RESEND_TIMEOUT);
+        _context.simpleTimer2().addEvent(new ContinueTest(test.getNonce()), RESEND_TIMEOUT);
     }
     
     private class ContinueTest implements SimpleTimer.TimedEvent {
@@ -246,7 +246,7 @@ class PeerTestManager {
                         sendTestToCharlie();
                     }
                     // retx at 4, 10, 17, 25 elapsed time
-                    _context.simpleScheduler().addEvent(ContinueTest.this, RESEND_TIMEOUT + (sent*1000));
+                    _context.simpleTimer2().addEvent(ContinueTest.this, RESEND_TIMEOUT + (sent*1000));
                 }
             }
         }
@@ -702,7 +702,7 @@ class PeerTestManager {
             
             if (isNew) {
                 _activeTests.put(Long.valueOf(nonce), state);
-                _context.simpleScheduler().addEvent(new RemoveTest(nonce), MAX_CHARLIE_LIFETIME);
+                _context.simpleTimer2().addEvent(new RemoveTest(nonce), MAX_CHARLIE_LIFETIME);
             }
 
             UDPPacket packet = _packetBuilder.buildPeerTestToBob(bobIP, from.getPort(), aliceIP, alicePort,
@@ -805,7 +805,7 @@ class PeerTestManager {
             
             if (isNew) {
                 _activeTests.put(Long.valueOf(nonce), state);
-                _context.simpleScheduler().addEvent(new RemoveTest(nonce), MAX_CHARLIE_LIFETIME);
+                _context.simpleTimer2().addEvent(new RemoveTest(nonce), MAX_CHARLIE_LIFETIME);
             }
             
             UDPPacket packet = _packetBuilder.buildPeerTestToCharlie(aliceIP, from.getPort(), aliceIntroKey, nonce, 
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
index 391a703b2ecb23264980739cadda0dcac60b94d0..6a53d8551c623f8d213e8725f32d0f6b4c890d70 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
@@ -270,7 +270,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
         //_context.statManager().createRateStat("udp.packetAuthTime", "How long it takes to encrypt and MAC a packet for sending", "udp", RATES);
         //_context.statManager().createRateStat("udp.packetAuthTimeSlow", "How long it takes to encrypt and MAC a packet for sending (when its slow)", "udp", RATES);
 
-        _context.simpleScheduler().addPeriodicEvent(new PingIntroducers(), MIN_EXPIRE_TIMEOUT * 3 / 4);
+        _context.simpleTimer2().addPeriodicEvent(new PingIntroducers(), MIN_EXPIRE_TIMEOUT * 3 / 4);
     }
     
     /**
@@ -1235,7 +1235,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
                     RemoteHostId remote = peer.getRemoteHostId();
                     _dropList.add(remote);
                     _context.statManager().addRateData("udp.dropPeerDroplist", 1);
-                    _context.simpleScheduler().addEvent(new RemoveDropList(remote), DROPLIST_PERIOD);
+                    _context.simpleTimer2().addEvent(new RemoveDropList(remote), DROPLIST_PERIOD);
                 }
                 markUnreachable(peerHash);
                 _context.banlist().banlistRouter(peerHash, "Part of the wrong network, version = " + ((RouterInfo) entry).getVersion());
diff --git a/router/java/src/net/i2p/router/tunnel/TunnelGatewayPumper.java b/router/java/src/net/i2p/router/tunnel/TunnelGatewayPumper.java
index 17b9a9a8c5f4c8e0a178346e2867e2d4a5cdd15f..6621183f9a9284360b864783a8664b79563e8382 100644
--- a/router/java/src/net/i2p/router/tunnel/TunnelGatewayPumper.java
+++ b/router/java/src/net/i2p/router/tunnel/TunnelGatewayPumper.java
@@ -84,7 +84,7 @@ class TunnelGatewayPumper implements Runnable {
                         // in case another packet came in
                         _wantsPumping.remove(gw);
                         if (_backlogged.add(gw))
-                            _context.simpleScheduler().addEvent(new Requeue(gw), REQUEUE_TIME);
+                            _context.simpleTimer2().addEvent(new Requeue(gw), REQUEUE_TIME);
                     }
                     gw = null;
                     if (_wantsPumping.isEmpty()) {
diff --git a/router/java/src/net/i2p/router/tunnel/pool/ParticipatingThrottler.java b/router/java/src/net/i2p/router/tunnel/pool/ParticipatingThrottler.java
index e1e54764942990bf8155f19a1444206790a33f72..e3d877725d1ccfed393fe7c41d954ae47070d2dd 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/ParticipatingThrottler.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/ParticipatingThrottler.java
@@ -41,7 +41,7 @@ class ParticipatingThrottler {
     ParticipatingThrottler(RouterContext ctx) {
         this.context = ctx;
         this.counter = new ObjectCounter<Hash>();
-        ctx.simpleScheduler().addPeriodicEvent(new Cleaner(), CLEAN_TIME);
+        ctx.simpleTimer2().addPeriodicEvent(new Cleaner(), CLEAN_TIME);
     }
 
     /** increments before checking */
diff --git a/router/java/src/net/i2p/router/tunnel/pool/RequestThrottler.java b/router/java/src/net/i2p/router/tunnel/pool/RequestThrottler.java
index 8c3a3c8c6f2cc57793b9dd33408ec771ad187bac..794808408b5c4993edf2f873aca3cfcbfc4c5cf6 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/RequestThrottler.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/RequestThrottler.java
@@ -26,7 +26,7 @@ class RequestThrottler {
     RequestThrottler(RouterContext ctx) {
         this.context = ctx;
         this.counter = new ObjectCounter<Hash>();
-        ctx.simpleScheduler().addPeriodicEvent(new Cleaner(), CLEAN_TIME);
+        ctx.simpleTimer2().addPeriodicEvent(new Cleaner(), CLEAN_TIME);
     }
 
     /** increments before checking */
diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java
index d311d46a9679b495509c5646ee4fba5888637efb..4cf54cb69d984f86812aff4ad69f7bfb06de1b3e 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java
@@ -430,7 +430,7 @@ public class TunnelPoolManager implements TunnelManagerFacade {
         // don't delay the outbound if it already exists, as this opens up a large
         // race window with removeTunnels() below
         if (delayOutbound)
-            _context.simpleScheduler().addEvent(new DelayedStartup(outbound), 1000);
+            _context.simpleTimer2().addEvent(new DelayedStartup(outbound), 1000);
         else
             outbound.startup();
     }
@@ -512,7 +512,7 @@ public class TunnelPoolManager implements TunnelManagerFacade {
         }
         
         _inboundExploratory.startup();
-        _context.simpleScheduler().addEvent(new DelayedStartup(_outboundExploratory), 3*1000);
+        _context.simpleTimer2().addEvent(new DelayedStartup(_outboundExploratory), 3*1000);
         
         // try to build up longer tunnels
         _context.jobQueue().addJob(new BootstrapPool(_context, _inboundExploratory));