Index: trunk/src/org/openstreetmap/josm/data/osm/DataSet.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 10807)
+++ trunk/src/org/openstreetmap/josm/data/osm/DataSet.java	(revision 10809)
@@ -752,6 +752,5 @@
 
         highlightedVirtualNodes = waySegments;
-        // can't use fireHighlightingChanged because it requires an OsmPrimitive
-        highlightUpdateCount++;
+        fireHighlightingChanged();
     }
 
@@ -765,6 +764,5 @@
 
         highlightedWaySegments = waySegments;
-        // can't use fireHighlightingChanged because it requires an OsmPrimitive
-        highlightUpdateCount++;
+        fireHighlightingChanged();
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/MapView.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MapView.java	(revision 10807)
+++ trunk/src/org/openstreetmap/josm/gui/MapView.java	(revision 10809)
@@ -26,4 +26,5 @@
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.IdentityHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -148,7 +149,15 @@
     private class LayerInvalidatedListener implements PaintableInvalidationListener {
         private boolean ignoreRepaint;
+
+        private final Set<MapViewPaintable> invalidatedLayers = Collections.newSetFromMap(new IdentityHashMap<MapViewPaintable, Boolean>());
+
         @Override
         public void paintableInvalidated(PaintableInvalidationEvent event) {
+            invalidate(event.getLayer());
+        }
+
+        public synchronized void invalidate(MapViewPaintable mapViewPaintable) {
             ignoreRepaint = true;
+            invalidatedLayers.add(mapViewPaintable);
             repaint();
         }
@@ -158,5 +167,5 @@
          * @param p The paintable.
          */
-        public void addTo(MapViewPaintable p) {
+        public synchronized void addTo(MapViewPaintable p) {
             if (p instanceof AbstractMapViewPaintable) {
                 ((AbstractMapViewPaintable) p).addInvalidationListener(this);
@@ -168,8 +177,9 @@
          * @param p The paintable.
          */
-        public void removeFrom(MapViewPaintable p) {
+        public synchronized void removeFrom(MapViewPaintable p) {
             if (p instanceof AbstractMapViewPaintable) {
                 ((AbstractMapViewPaintable) p).removeInvalidationListener(this);
             }
+            invalidatedLayers.remove(p);
         }
 
@@ -183,4 +193,15 @@
             }
             ignoreRepaint = false;
+        }
+
+        /**
+         * Retrieves a set of all layers that have been marked as invalid since the last call to this method.
+         * @return The layers
+         */
+        protected synchronized Set<MapViewPaintable> collectInvalidatedLayers() {
+            Set<MapViewPaintable> layers = Collections.newSetFromMap(new IdentityHashMap<MapViewPaintable, Boolean>());
+            layers.addAll(invalidatedLayers);
+            invalidatedLayers.clear();
+            return layers;
         }
     }
@@ -504,5 +525,4 @@
     // Layers that wasn't changed since last paint
     private final transient List<Layer> nonChangedLayers = new ArrayList<>();
-    private transient Layer changedLayer;
     private int lastViewID;
     private boolean paintPreferencesChanged = true;
@@ -842,6 +862,7 @@
 
         int nonChangedLayersCount = 0;
+        Set<MapViewPaintable> invalidated = invalidatedListener.collectInvalidatedLayers();
         for (Layer l: visibleLayers) {
-            if (l.isChanged() || l == changedLayer) {
+            if (l.isChanged() || invalidated.contains(l)) {
                 break;
             } else {
@@ -900,5 +921,4 @@
 
         nonChangedLayers.clear();
-        changedLayer = null;
         for (int i = 0; i < nonChangedLayersCount; i++) {
             nonChangedLayers.add(visibleLayers.get(i));
@@ -1205,6 +1225,5 @@
             Layer l = (Layer) evt.getSource();
             if (l.isVisible()) {
-                changedLayer = l;
-                repaint();
+                invalidatedListener.invalidate(l);
             }
         }
Index: trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 10807)
+++ trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 10809)
@@ -51,4 +51,5 @@
 import javax.swing.JSeparator;
 import javax.swing.JTextField;
+import javax.swing.Timer;
 
 import org.openstreetmap.gui.jmapviewer.AttributionSupport;
@@ -137,5 +138,4 @@
      */
     public int currentZoomLevel;
-    private boolean needRedraw;
 
     private final AttributionSupport attribution = new AttributionSupport();
@@ -158,4 +158,9 @@
     protected T tileSource;
     protected TileLoader tileLoader;
+
+    /**
+     * A timer that is used to delay invalidation events if required.
+     */
+    private final Timer invalidateLaterTimer = new Timer(100, e -> this.invalidate());
 
     private final MouseAdapter adapter = new MouseAdapter() {
@@ -264,8 +269,5 @@
         }
         tile.setLoaded(success);
-        needRedraw = true;
-        if (Main.map != null) {
-            Main.map.repaint(100);
-        }
+        invalidateLater();
         if (Main.isDebugEnabled()) {
             Main.debug("tileLoadingFinished() tile: " + tile + " success: " + success);
@@ -297,12 +299,5 @@
      */
     protected void redraw() {
-        needRedraw = true;
-        if (isVisible()) Main.map.repaint();
-    }
-
-    @Override
-    public void invalidate() {
-        needRedraw = true;
-        super.invalidate();
+        invalidate();
     }
 
@@ -1030,10 +1025,26 @@
     public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
         boolean done = (infoflags & (ERROR | FRAMEBITS | ALLBITS)) != 0;
-        needRedraw = true;
         if (Main.isDebugEnabled()) {
             Main.debug("imageUpdate() done: " + done + " calling repaint");
         }
-        Main.map.repaint(done ? 0 : 100);
+
+        if (done) {
+            invalidate();
+        } else {
+            invalidateLater();
+        }
         return !done;
+    }
+
+    /**
+     * Invalidate the layer at a time in the future so taht the user still sees the interface responsive.
+     */
+    private void invalidateLater() {
+        GuiHelper.runInEDT(() -> {
+            if (!invalidateLaterTimer.isRunning()) {
+                invalidateLaterTimer.setRepeats(false);
+                invalidateLaterTimer.start();
+            }
+        });
     }
 
@@ -1753,5 +1764,6 @@
     @Override
     public boolean isChanged() {
-        return needRedraw;
+        // we use #invalidate()
+        return false;
     }
 
@@ -1899,6 +1911,4 @@
             ProjectionBounds pb = graphics.getClipBounds().getProjectionBounds();
 
-            needRedraw = false; // TEMPORARY
-
             drawInViewArea(graphics.getDefaultGraphics(), graphics.getMapView(), pb);
         }
Index: trunk/src/org/openstreetmap/josm/gui/layer/Layer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/Layer.java	(revision 10807)
+++ trunk/src/org/openstreetmap/josm/gui/layer/Layer.java	(revision 10809)
@@ -426,5 +426,9 @@
      *
      * @return True if layer was changed since last paint
-     */
+     * @deprecated This is not supported by multiple map views.
+     * Fire an {@link #invalidate()} to trigger a repaint.
+     * Let this method return false if you only use invalidation events.
+     */
+    @Deprecated
     public boolean isChanged() {
         return true;
