diff --git a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
index 523690764e123d640911a4c76a32bc33846a7792..35145418aaacb4b9f8cdedd21dec095fc6c4d2a4 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
@@ -342,6 +342,7 @@ public class RouterConsoleRunner {
         }
 
         Thread t = new I2PAppThread(new StatSummarizer(), "StatSummarizer", true);
+        t.setPriority(Thread.NORM_PRIORITY - 1);
         t.start();
         
         List<RouterContext> contexts = RouterContext.listContexts();
@@ -350,10 +351,12 @@ public class RouterConsoleRunner {
 
             NewsFetcher fetcher = NewsFetcher.getInstance(ctx);
             Thread newsThread = new I2PAppThread(fetcher, "NewsFetcher", true);
+            newsThread.setPriority(Thread.NORM_PRIORITY - 1);
             newsThread.start();
         
             if (PluginStarter.pluginsEnabled(ctx)) {
                 t = new I2PAppThread(new PluginStarter(ctx), "PluginStarter", true);
+                t.setPriority(Thread.NORM_PRIORITY - 1);
                 t.start();
                 ctx.addShutdownTask(new PluginStopper(ctx));
             }
diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java
index 5fd7452cf4f8cc9d65f0cd68f5a01bf0f3aff31e..6599a00d7b3d6d0a4ac9c149877b5da5350548ce 100644
--- a/router/java/src/net/i2p/router/Router.java
+++ b/router/java/src/net/i2p/router/Router.java
@@ -275,32 +275,16 @@ public class Router implements RouterClock.ClockShiftListener {
         _log.info("New router created with config file " + _configFilename);
         //_sessionKeyPersistenceHelper = new SessionKeyPersistenceHelper(_context);
         _killVMOnEnd = true;
-        _oomListener = new I2PThread.OOMEventListener() { 
-            public void outOfMemory(OutOfMemoryError oom) { 
-                clearCaches();
-                _log.log(Log.CRIT, "Thread ran out of memory, shutting down I2P", oom);
-                // prevent multiple parallel shutdowns (when you OOM, you OOM a lot...)
-                if (_shutdownInProgress)
-                    return;
-                for (int i = 0; i < 5; i++) { // try this 5 times, in case it OOMs
-                    try { 
-                        _log.log(Log.CRIT, "free mem: " + Runtime.getRuntime().freeMemory() + 
-                                           " total mem: " + Runtime.getRuntime().totalMemory());
-                        break; // w00t
-                    } catch (OutOfMemoryError oome) {
-                        // gobble
-                    }
-                }
-                _log.log(Log.CRIT, "To prevent future shutdowns, increase wrapper.java.maxmemory in $I2P/wrapper.config");
-                shutdown(EXIT_OOM); 
-            }
-        };
+        _oomListener = new OOMListener(_context);
+
         _shutdownHook = new ShutdownHook(_context);
-        _gracefulShutdownDetector = new I2PAppThread(new GracefulShutdown(), "Graceful shutdown hook", true);
+        _gracefulShutdownDetector = new I2PAppThread(new GracefulShutdown(_context), "Graceful shutdown hook", true);
+        _gracefulShutdownDetector.setPriority(Thread.NORM_PRIORITY + 1);
         _gracefulShutdownDetector.start();
         
         _watchdog = new RouterWatchdog(_context);
         _watchdogThread = new I2PAppThread(_watchdog, "RouterWatchdog", true);
+        _watchdogThread.setPriority(Thread.NORM_PRIORITY + 1);
         _watchdogThread.start();
         
     }
@@ -973,27 +957,6 @@ public class Router implements RouterClock.ClockShiftListener {
     }
 ******/
 
-    /**
-     *  A non-daemon thread to let
-     *  the shutdown task get all the way to the end
-     *  @since 0.8.8
-     */
-    private static class Spinner extends Thread {
-
-        public Spinner() {
-            super();
-            setName("Shutdown Spinner");
-            setDaemon(false);
-        }
-
-        @Override
-        public void run() {
-            try {
-                sleep(60*1000);
-            } catch (InterruptedException ie) {}
-        }
-    }
-    
     public static final int EXIT_GRACEFUL = 2;
     public static final int EXIT_HARD = 3;
     public static final int EXIT_OOM = 10;
@@ -1170,13 +1133,27 @@ public class Router implements RouterClock.ClockShiftListener {
             _gracefulShutdownDetector.notifyAll();
         }        
     }
+
     /**
      * What exit code do we plan on using when we shut down (or -1, if there isn't a graceful shutdown planned)
      */
     public int scheduledGracefulExitCode() { return _gracefulExitCode; }
+
+    /**
+     * Is a graceful shutdown in progress? This may be cancelled.
+     */
     public boolean gracefulShutdownInProgress() {
         return (null != _config.get(PROP_SHUTDOWN_IN_PROGRESS));
     }
+
+    /**
+     * Is a final shutdown in progress? This may not be cancelled.
+     * @since 0.8.12
+     */
+    public boolean isFinalShutdownInProgress() {
+        return _shutdownInProgress;
+    }
+
     /** How long until the graceful shutdown will kill us?  */
     public long getShutdownTimeRemaining() {
         if (_gracefulExitCode <= 0) return -1; // maybe Long.MAX_VALUE would be better?
@@ -1189,51 +1166,6 @@ public class Router implements RouterClock.ClockShiftListener {
             return exp + 2*CLOCK_FUDGE_FACTOR - _context.clock().now();
     }
     
-    /**
-     * Simple thread that sits and waits forever, managing the
-     * graceful shutdown "process" (describing it would take more text
-     * than just reading the code...)
-     *
-     */
-    private class GracefulShutdown implements Runnable {
-        public void run() {
-            while (true) {
-                boolean shutdown = (null != _config.get(PROP_SHUTDOWN_IN_PROGRESS));
-                if (shutdown) {
-                    if (_gracefulExitCode == EXIT_HARD || _gracefulExitCode == EXIT_HARD_RESTART ||
-                        _context.tunnelManager().getParticipatingCount() <= 0) {
-                        if (_gracefulExitCode == EXIT_HARD)
-                            _log.log(Log.CRIT, "Shutting down after a brief delay");
-                        else if (_gracefulExitCode == EXIT_HARD_RESTART)
-                            _log.log(Log.CRIT, "Restarting after a brief delay");
-                        else
-                            _log.log(Log.CRIT, "Graceful shutdown progress - no more tunnels, safe to die");
-                        // Allow time for a UI reponse
-                        try {
-                            synchronized (Thread.currentThread()) {
-                                Thread.currentThread().wait(2*1000);
-                            }
-                        } catch (InterruptedException ie) {}
-                        shutdown(_gracefulExitCode);
-                        return;
-                    } else {
-                        try {
-                            synchronized (Thread.currentThread()) {
-                                Thread.currentThread().wait(10*1000);
-                            }
-                        } catch (InterruptedException ie) {}
-                    }
-                } else {
-                    try {
-                        synchronized (Thread.currentThread()) {
-                            Thread.currentThread().wait();
-                        }
-                    } catch (InterruptedException ie) {}
-                }
-            }
-        }
-    }
-    
     /**
      * Save the current config options (returning true if save was 
      * successful, false otherwise)
@@ -1308,48 +1240,20 @@ public class Router implements RouterClock.ClockShiftListener {
             return;
         ((RouterClock) _context.clock()).removeShiftListener(this);
         _isAlive = false;
-        Thread t = new Thread(new Restarter(), "Router Restart");
+        _started = _context.clock().now();
+        Thread t = new Thread(new Restarter(_context), "Router Restart");
+        t.setPriority(Thread.NORM_PRIORITY + 1);
         t.start();
     }    
 
     /**
-     *  @since 0.8.8
+     *  Only for Restarter
+     *  @since 0.8.12
      */
-    private class Restarter implements Runnable {
-        public void run() {
-            _started = _context.clock().now();
-            _log.error("Stopping the router for a restart...");
-            _log.logAlways(Log.WARN, "Stopping the client manager");
-            // NOTE: DisconnectMessageHandler keys off "restart"
-            try { _context.clientManager().shutdown("Router restart"); } catch (Throwable t) { _log.log(Log.CRIT, "Error stopping the client manager", t); }
-            _log.logAlways(Log.WARN, "Stopping the comm system");
-            _context.bandwidthLimiter().reinitialize();
-            try { _context.messageRegistry().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the message registry", t); }
-            try { _context.commSystem().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the comm system", t); }
-            _log.logAlways(Log.WARN, "Stopping the tunnel manager");
-            try { _context.tunnelManager().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the tunnel manager", t); }
-
-            //try { _context.peerManager().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the peer manager", t); }
-            //try { _context.netDb().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the networkDb", t); }
-            //try { _context.jobQueue().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the job queue", t); }
-        
-            _log.logAlways(Log.WARN, "Router teardown complete, restarting the router...");
-            try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
-        
-            _log.logAlways(Log.WARN, "Restarting the comm system");
-            _log.logAlways(Log.WARN, "Restarting the tunnel manager");
-            _log.logAlways(Log.WARN, "Restarting the client manager");
-            try { _context.clientMessagePool().restart(); } catch (Throwable t) { _log.log(Log.CRIT, "Error restarting the CMP", t); }
-            try { _context.clientManager().startup(); } catch (Throwable t) { _log.log(Log.CRIT, "Error starting the client manager", t); }
-        
-            _isAlive = true;
-            rebuildRouterInfo();
-        
-            _log.logAlways(Log.WARN, "Restart complete");
-            ((RouterClock) _context.clock()).addShiftListener(Router.this);
-        }
+    public void setIsAlive() {
+        _isAlive = true;
     }
-    
+
     public static void main(String args[]) {
         System.out.println("Starting I2P " + RouterVersion.FULL_VERSION);
         // installUpdates() moved to constructor so we can get file locations from the context
diff --git a/router/java/src/net/i2p/router/tasks/GracefulShutdown.java b/router/java/src/net/i2p/router/tasks/GracefulShutdown.java
new file mode 100644
index 0000000000000000000000000000000000000000..c2ded9b49131698f975243a8be8c3b77e66f53ce
--- /dev/null
+++ b/router/java/src/net/i2p/router/tasks/GracefulShutdown.java
@@ -0,0 +1,60 @@
+package net.i2p.router.tasks;
+
+import net.i2p.router.Router;
+import net.i2p.router.RouterContext;
+import net.i2p.util.Log;
+
+/**
+ * Simple thread that sits and waits forever, managing the
+ * graceful shutdown "process" (describing it would take more text
+ * than just reading the code...)
+ *
+ *  @since 0.8.12 moved from Router
+ */
+public class GracefulShutdown implements Runnable {
+    private final RouterContext _context;
+
+    public GracefulShutdown(RouterContext ctx) {
+        _context = ctx;
+    }
+
+    public void run() {
+        Log log = _context.logManager().getLog(Router.class);
+        while (true) {
+            boolean shutdown = _context.router().gracefulShutdownInProgress();
+            if (shutdown) {
+                int gracefulExitCode = _context.router().scheduledGracefulExitCode();
+                if (gracefulExitCode == Router.EXIT_HARD || gracefulExitCode == Router.EXIT_HARD_RESTART ||
+                    _context.tunnelManager().getParticipatingCount() <= 0) {
+                    if (gracefulExitCode == Router.EXIT_HARD)
+                        log.log(Log.CRIT, "Shutting down after a brief delay");
+                    else if (gracefulExitCode == Router.EXIT_HARD_RESTART)
+                        log.log(Log.CRIT, "Restarting after a brief delay");
+                    else
+                        log.log(Log.CRIT, "Graceful shutdown progress - no more tunnels, safe to die");
+                    // Allow time for a UI reponse
+                    try {
+                        synchronized (Thread.currentThread()) {
+                            Thread.currentThread().wait(2*1000);
+                        }
+                    } catch (InterruptedException ie) {}
+                    _context.router().shutdown(gracefulExitCode);
+                    return;
+                } else {
+                    try {
+                        synchronized (Thread.currentThread()) {
+                            Thread.currentThread().wait(10*1000);
+                        }
+                    } catch (InterruptedException ie) {}
+                }
+            } else {
+                try {
+                    synchronized (Thread.currentThread()) {
+                        Thread.currentThread().wait();
+                    }
+                } catch (InterruptedException ie) {}
+            }
+        }
+    }
+}
+
diff --git a/router/java/src/net/i2p/router/tasks/OOMListener.java b/router/java/src/net/i2p/router/tasks/OOMListener.java
new file mode 100644
index 0000000000000000000000000000000000000000..a9650c7bd2043d61e849217c2986dced3617d2c2
--- /dev/null
+++ b/router/java/src/net/i2p/router/tasks/OOMListener.java
@@ -0,0 +1,39 @@
+package net.i2p.router.tasks;
+
+import net.i2p.router.Router;
+import net.i2p.router.RouterContext;
+import net.i2p.util.I2PThread;
+import net.i2p.util.Log;
+
+/**
+ *  Kaboom
+ *
+ *  @since 0.8.12 moved from Router.java
+ */
+public class OOMListener implements I2PThread.OOMEventListener { 
+    private final RouterContext _context;
+
+    public OOMListener(RouterContext ctx) {
+        _context = ctx;
+    }
+
+    public void outOfMemory(OutOfMemoryError oom) { 
+        Router.clearCaches();
+        Log log = _context.logManager().getLog(Router.class);
+        log.log(Log.CRIT, "Thread ran out of memory, shutting down I2P", oom);
+        // prevent multiple parallel shutdowns (when you OOM, you OOM a lot...)
+        if (_context.router().isFinalShutdownInProgress())
+            return;
+        for (int i = 0; i < 5; i++) { // try this 5 times, in case it OOMs
+            try { 
+                log.log(Log.CRIT, "free mem: " + Runtime.getRuntime().freeMemory() + 
+                                  " total mem: " + Runtime.getRuntime().totalMemory());
+                break; // w00t
+            } catch (OutOfMemoryError oome) {
+                // gobble
+            }
+        }
+        log.log(Log.CRIT, "To prevent future shutdowns, increase wrapper.java.maxmemory in $I2P/wrapper.config");
+        _context.router().shutdown(Router.EXIT_OOM); 
+    }
+}
diff --git a/router/java/src/net/i2p/router/tasks/Restarter.java b/router/java/src/net/i2p/router/tasks/Restarter.java
new file mode 100644
index 0000000000000000000000000000000000000000..2d497abb4b4a7fbc54735d7197d77fef79141ad2
--- /dev/null
+++ b/router/java/src/net/i2p/router/tasks/Restarter.java
@@ -0,0 +1,51 @@
+package net.i2p.router.tasks;
+
+import net.i2p.router.Router;
+import net.i2p.router.RouterClock;
+import net.i2p.router.RouterContext;
+import net.i2p.util.Log;
+
+/**
+ *  @since 0.8.8, moved from Router in 0.8.12
+ */
+public class Restarter implements Runnable {
+    private final RouterContext _context;
+
+    public Restarter(RouterContext ctx) {
+        _context = ctx;
+    }
+
+    public void run() {
+        Log log = _context.logManager().getLog(Router.class);
+        log.error("Stopping the router for a restart...");
+        log.logAlways(Log.WARN, "Stopping the client manager");
+        // NOTE: DisconnectMessageHandler keys off "restart"
+        try { _context.clientManager().shutdown("Router restart"); } catch (Throwable t) { log.log(Log.CRIT, "Error stopping the client manager", t); }
+        log.logAlways(Log.WARN, "Stopping the comm system");
+        _context.bandwidthLimiter().reinitialize();
+        try { _context.messageRegistry().restart(); } catch (Throwable t) { log.log(Log.CRIT, "Error restarting the message registry", t); }
+        try { _context.commSystem().restart(); } catch (Throwable t) { log.log(Log.CRIT, "Error restarting the comm system", t); }
+        log.logAlways(Log.WARN, "Stopping the tunnel manager");
+        try { _context.tunnelManager().restart(); } catch (Throwable t) { log.log(Log.CRIT, "Error restarting the tunnel manager", t); }
+
+        //try { _context.peerManager().restart(); } catch (Throwable t) { log.log(Log.CRIT, "Error restarting the peer manager", t); }
+        //try { _context.netDb().restart(); } catch (Throwable t) { log.log(Log.CRIT, "Error restarting the networkDb", t); }
+        //try { _context.jobQueue().restart(); } catch (Throwable t) { log.log(Log.CRIT, "Error restarting the job queue", t); }
+    
+        log.logAlways(Log.WARN, "Router teardown complete, restarting the router...");
+        try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
+    
+        log.logAlways(Log.WARN, "Restarting the comm system");
+        log.logAlways(Log.WARN, "Restarting the tunnel manager");
+        log.logAlways(Log.WARN, "Restarting the client manager");
+        try { _context.clientMessagePool().restart(); } catch (Throwable t) { log.log(Log.CRIT, "Error restarting the CMP", t); }
+        try { _context.clientManager().startup(); } catch (Throwable t) { log.log(Log.CRIT, "Error starting the client manager", t); }
+    
+        _context.router().setIsAlive();
+        _context.router().rebuildRouterInfo();
+    
+        log.logAlways(Log.WARN, "Restart complete");
+        ((RouterClock) _context.clock()).addShiftListener(_context.router());
+    }
+}
+