diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java
index d217e699489ac34e6d300f07a85d41c17e58b225..afa92ac6e16d31b9413d913db471f5844b5fc2d0 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java
@@ -33,6 +33,8 @@ public class TunnelPool {
     private int _buildsThisMinute;
     private long _currentMinute;
     private RefreshJob _refreshJob;
+    private TunnelInfo _lastSelected;
+    private long _lastSelectionPeriod;
     
     /**
      * Only 3 builds per minute per pool, even if we have failing tunnels,
@@ -50,6 +52,8 @@ public class TunnelPool {
         _peerSelector = sel;
         _builder = builder;
         _alive = false;
+        _lastSelectionPeriod = 0;
+        _lastSelected = null;
         _lifetimeProcessed = 0;
         _buildsThisMinute = 0;
         _currentMinute = ctx.clock().now();
@@ -77,6 +81,8 @@ public class TunnelPool {
     }
     public void shutdown() {
         _alive = false;
+        _lastSelectionPeriod = 0;
+        _lastSelected = null;
     }
 
     private int countUsableTunnels() {
@@ -153,6 +159,12 @@ public class TunnelPool {
         }
     }
     
+    /** 
+     * when selecting tunnels, stick with the same one for a brief 
+     * period to allow batching if we can.
+     */
+    private static final long SELECTION_PERIOD = 500;
+    
     /**
      * Pull a random tunnel out of the pool.  If there are none available but
      * the pool is configured to allow 0hop tunnels, this builds a fake one
@@ -161,7 +173,18 @@ public class TunnelPool {
      */
     public TunnelInfo selectTunnel() { return selectTunnel(true); }
     private TunnelInfo selectTunnel(boolean allowRecurseOnFail) {
+        long period = _context.clock().now();
+        period -= period % SELECTION_PERIOD;
         synchronized (_tunnels) {
+            if (_lastSelectionPeriod == period) {
+                if ( (_lastSelected != null) && 
+                     (_lastSelected.getExpiration() > period) &&
+                     (_tunnels.contains(_lastSelected)) )
+                    return _lastSelected;
+            }
+            _lastSelectionPeriod = period;
+            _lastSelected = null;
+
             if (_tunnels.size() <= 0) {
                 if (_log.shouldLog(Log.WARN))
                     _log.warn(toString() + ": No tunnels to select from");
@@ -172,6 +195,7 @@ public class TunnelPool {
                     TunnelInfo info = (TunnelInfo)_tunnels.get(i);
                     if (info.getExpiration() > _context.clock().now()) {
                         //_log.debug("Selecting tunnel: " + info + " - " + _tunnels);
+                        _lastSelected = info;
                         return info;
                     }
                 }
@@ -261,6 +285,10 @@ public class TunnelPool {
             if (_settings.isInbound() && (_settings.getDestination() != null) )
                 ls = locked_buildNewLeaseSet();
             remaining = _tunnels.size();
+            if (_lastSelected == info) {
+                _lastSelected = null;
+                _lastSelectionPeriod = 0;
+            }
         }
         
         _lifetimeProcessed += info.getProcessedMessagesCount();
@@ -297,6 +325,10 @@ public class TunnelPool {
             if (_settings.isInbound() && (_settings.getDestination() != null) )
                 ls = locked_buildNewLeaseSet();
             remaining = _tunnels.size();
+            if (_lastSelected == cfg) {
+                _lastSelected = null;
+                _lastSelectionPeriod = 0;
+            }
         }
         
         _lifetimeProcessed += cfg.getProcessedMessagesCount();