diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
index ad5256fa65b4893ce578d05319d07bb6627bef01..1b7f4c926eb0c2c0d778ca142d38e4f307a697e5 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
@@ -42,8 +42,17 @@ public class TunnelController implements Logging {
     private final I2PTunnel _tunnel;
     private final List<String> _messages;
     private List<I2PSession> _sessions;
-    private boolean _running;
-    private boolean _starting;
+    private volatile TunnelState _state;
+
+    private enum TunnelState {
+        START_ON_LOAD,
+        STARTING,
+        RUNNING,
+        STOPPING,
+        STOPPED,
+        DESTROYING,
+        DESTROYED,
+    }
     
     public static final String KEY_BACKUP_DIR = "i2ptunnel-keyBackup";
 
@@ -124,11 +133,10 @@ public class TunnelController implements Logging {
         _log = I2PAppContext.getGlobalContext().logManager().getLog(TunnelController.class);
         setConfig(config, prefix);
         _messages = new ArrayList<String>(4);
-        _running = false;
         boolean keyOK = true;
         if (createKey && (getType().endsWith("server") || getPersistentClientKey()))
             keyOK = createPrivateKey();
-        _starting = keyOK && getStartOnLoad();
+        _state = keyOK && getStartOnLoad() ? TunnelState.START_ON_LOAD : TunnelState.STOPPED;
     }
     
     /**
@@ -193,8 +201,10 @@ public class TunnelController implements Logging {
     }
     
     public void startTunnelBackground() {
-        if (_running) return;
-        _starting = true;
+        synchronized (this) {
+            if (_state != TunnelState.STOPPED && _state != TunnelState.START_ON_LOAD)
+                return;
+        }
         new I2PAppThread(new Runnable() { public void run() { startTunnel(); } }).start();
     }
     
@@ -203,7 +213,17 @@ public class TunnelController implements Logging {
      *
      */
     public void startTunnel() {
-        _starting = true;
+        synchronized (this) {
+            if (_state != TunnelState.STOPPED && _state != TunnelState.START_ON_LOAD) {
+                if (_state == TunnelState.RUNNING) {
+                    if (_log.shouldLog(Log.INFO))
+                        _log.info("Already running");
+                    log("Tunnel " + getName() + " is already running");
+                }
+                return;
+            }
+            changeState(TunnelState.STARTING);
+        }
         try {
             doStartTunnel();
         } catch (Exception e) {
@@ -213,21 +233,20 @@ public class TunnelController implements Logging {
             acquire();
             stopTunnel();
         }
-        _starting = false;
     }
 
     /**
      *  @throws IllegalArgumentException via methods in I2PTunnel
      */
     private void doStartTunnel() {
-        if (_running) {
-            if (_log.shouldLog(Log.INFO))
-                _log.info("Already running");
-            log("Tunnel " + getName() + " is already running");
-            return;
+        synchronized (this) {
+            if (_state != TunnelState.STARTING)
+                return;
         }
+
         String type = getType(); 
         if ( (type == null) || (type.length() <= 0) ) {
+            changeState(TunnelState.STOPPED);
             if (_log.shouldLog(Log.ERROR))
                 _log.error("Cannot start the tunnel - no type specified");
             return;
@@ -237,6 +256,7 @@ public class TunnelController implements Logging {
         if (type.endsWith("server") || getPersistentClientKey()) {
             boolean ok = createPrivateKey();
             if (!ok) {
+                changeState(TunnelState.STOPPED);
                 log("Failed to start tunnel " + getName() + " as the private key file could not be created");
                 return;
             }
@@ -268,12 +288,13 @@ public class TunnelController implements Logging {
         } else if (TYPE_STREAMR_SERVER.equals(type)) {
             startStreamrServer();
         } else {
+            changeState(TunnelState.STOPPED);
             if (_log.shouldLog(Log.ERROR))
                 _log.error("Cannot start tunnel - unknown type [" + type + "]");
             return;
         }
         acquire();
-        _running = true;
+        changeState(TunnelState.RUNNING);
     }
     
     private void startHttpClient() {
@@ -554,12 +575,17 @@ public class TunnelController implements Logging {
      *  and it may have timer threads that continue running.
      */
     public void stopTunnel() {
+        synchronized (this) {
+            if (_state != TunnelState.RUNNING)
+                return;
+            changeState(TunnelState.STOPPING);
+        }
         // I2PTunnel removes the session in close(),
         // so save the sessions to pass to release() and TCG
         Collection<I2PSession> sessions = getAllSessions();
         _tunnel.runClose(new String[] { "forced", "all" }, this);
         release(sessions);
-        _running = false;
+        changeState(TunnelState.STOPPED);
     }
 
     /**
@@ -569,12 +595,17 @@ public class TunnelController implements Logging {
      *  @since 0.9.17
      */
     public void destroyTunnel() {
+        synchronized (this) {
+            if (_state != TunnelState.RUNNING)
+                return;
+            changeState(TunnelState.DESTROYING);
+        }
         // I2PTunnel removes the session in close(),
         // so save the sessions to pass to release() and TCG
         Collection<I2PSession> sessions = getAllSessions();
         _tunnel.runClose(new String[] { "destroy", "all" }, this);
         release(sessions);
-        _running = false;
+        changeState(TunnelState.DESTROYED);
     }
     
     public void restartTunnel() {
@@ -626,26 +657,29 @@ public class TunnelController implements Logging {
         // tell i2ptunnel, who will tell the TunnelTask, who will tell the SocketManager
         setSessionOptions();
 
-        if (_running) {
-            Collection<I2PSession> sessions = getAllSessions();
-            if (sessions.isEmpty()) {
-                 if (_log.shouldLog(Log.DEBUG))
-                     _log.debug("Running but no sessions to update");
-            }
-            for (I2PSession s : sessions) {
-                // tell the router via the session
-                if (!s.isClosed()) {
-                    if (_log.shouldLog(Log.DEBUG))
-                        _log.debug("Session is open, updating: " + s);
-                    s.updateOptions(_tunnel.getClientOptions());
-                } else {
-                    if (_log.shouldLog(Log.DEBUG))
-                        _log.debug("Session is closed, not updating: " + s);
+        synchronized (this) {
+            if (_state != TunnelState.RUNNING) {
+                if (_log.shouldLog(Log.DEBUG)) {
+                    _log.debug("Not running, not updating sessions");
                 }
+                return;
             }
-        } else {
-            if (_log.shouldLog(Log.DEBUG)) {
-                _log.debug("Not running, not updating sessions");
+        }
+        // Running, so check sessions
+        Collection<I2PSession> sessions = getAllSessions();
+        if (sessions.isEmpty()) {
+             if (_log.shouldLog(Log.DEBUG))
+                 _log.debug("Running but no sessions to update");
+        }
+        for (I2PSession s : sessions) {
+            // tell the router via the session
+            if (!s.isClosed()) {
+                if (_log.shouldLog(Log.DEBUG))
+                    _log.debug("Session is open, updating: " + s);
+                s.updateOptions(_tunnel.getClientOptions());
+            } else {
+                if (_log.shouldLog(Log.DEBUG))
+                    _log.debug("Session is closed, not updating: " + s);
             }
         }
     }
@@ -794,19 +828,26 @@ public class TunnelController implements Logging {
         return null;
     }
     
-    public boolean getIsRunning() { return _running; }
-    public boolean getIsStarting() { return _starting; }
+    public boolean getIsRunning() { return _state == TunnelState.RUNNING; }
+    public boolean getIsStarting() { return _state == TunnelState.START_ON_LOAD || _state == TunnelState.STARTING; }
 
     /** if running but no open sessions, we are in standby */
     public boolean getIsStandby() {
-        if (!_running)
-            return false;
+        synchronized (this) {
+            if (_state != TunnelState.RUNNING)
+                return false;
+        }
+
         for (I2PSession sess : _tunnel.getSessions()) {
             if (!sess.isClosed())
                 return false;
         }
         return true;
     }
+
+    private synchronized void changeState(TunnelState state) {
+        _state = state;
+    }
     
     /**
      *  A text description of the tunnel.
@@ -927,7 +968,7 @@ public class TunnelController implements Logging {
      *
      */
     public void log(String s) {
-        synchronized (this) {
+        synchronized (_messages) {
             _messages.add(s);
             while (_messages.size() > 10)
                 _messages.remove(0);
@@ -943,7 +984,7 @@ public class TunnelController implements Logging {
      */
     public List<String> clearMessages() { 
         List<String> rv = null;
-        synchronized (this) {
+        synchronized (_messages) {
             rv = new ArrayList<String>(_messages);
             _messages.clear();
         }
@@ -955,6 +996,6 @@ public class TunnelController implements Logging {
      */
     @Override
     public String toString() { 
-        return "TC " + getType() + ' ' + getName() + " for " + _tunnel;
+        return "TC " + getType() + ' ' + getName() + " for " + _tunnel + ' ' + _state;
     }
 }