diff --git a/src/org/openstreetmap/josm/data/osm/DataSet.java b/src/org/openstreetmap/josm/data/osm/DataSet.java
index 608c9b5..4d24417 100644
--- a/src/org/openstreetmap/josm/data/osm/DataSet.java
+++ b/src/org/openstreetmap/josm/data/osm/DataSet.java
@@ -751,8 +751,7 @@ public final class DataSet implements Data, Cloneable, ProjectionChangeListener
             return;
 
         highlightedVirtualNodes = waySegments;
-        // can't use fireHighlightingChanged because it requires an OsmPrimitive
-        highlightUpdateCount++;
+        fireHighlightingChanged();
     }
 
     /**
@@ -764,8 +763,7 @@ public final class DataSet implements Data, Cloneable, ProjectionChangeListener
             return;
 
         highlightedWaySegments = waySegments;
-        // can't use fireHighlightingChanged because it requires an OsmPrimitive
-        highlightUpdateCount++;
+        fireHighlightingChanged();
     }
 
     /**
diff --git a/src/org/openstreetmap/josm/gui/MapView.java b/src/org/openstreetmap/josm/gui/MapView.java
index 9781528..8ea13ac 100644
--- a/src/org/openstreetmap/josm/gui/MapView.java
+++ b/src/org/openstreetmap/josm/gui/MapView.java
@@ -25,6 +25,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.IdentityHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
@@ -147,9 +148,17 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
      */
     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();
         }
 
@@ -157,7 +166,7 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
          * Temporary until all {@link MapViewPaintable}s support this.
          * @param p The paintable.
          */
-        public void addTo(MapViewPaintable p) {
+        public synchronized void addTo(MapViewPaintable p) {
             if (p instanceof AbstractMapViewPaintable) {
                 ((AbstractMapViewPaintable) p).addInvalidationListener(this);
             }
@@ -167,10 +176,11 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
          * Temporary until all {@link MapViewPaintable}s support this.
          * @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,6 +193,17 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
             }
             ignoreRepaint = false;
         }
+
+        /**
+         * Retrives 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;
+        }
     }
 
     /**
@@ -503,7 +524,6 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
     private transient BufferedImage offscreenBuffer;
     // 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;
     private Rectangle lastClipBounds = new Rectangle();
@@ -519,6 +539,8 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
      */
     private final HashMap<Layer, LayerPainter> registeredLayers = new HashMap<>();
 
+
+
     /**
      * Constructs a new {@code MapView}.
      * @param layerManager The layers to display.
@@ -841,8 +863,9 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
         List<Layer> visibleLayers = layerManager.getVisibleLayersInZOrder();
 
         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 {
                 nonChangedLayersCount++;
@@ -899,7 +922,6 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
         }
 
         nonChangedLayers.clear();
-        changedLayer = null;
         for (int i = 0; i < nonChangedLayersCount; i++) {
             nonChangedLayers.add(visibleLayers.get(i));
         }
@@ -1204,8 +1226,7 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
                 evt.getPropertyName().equals(Layer.FILTER_STATE_PROP)) {
             Layer l = (Layer) evt.getSource();
             if (l.isVisible()) {
-                changedLayer = l;
-                repaint();
+                invalidatedListener.invalidate(l);
             }
         }
     }
diff --git a/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java b/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
index c26acdd..8faa3fc 100644
--- a/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
+++ b/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
@@ -50,6 +50,7 @@ import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
 import javax.swing.JSeparator;
 import javax.swing.JTextField;
+import javax.swing.Timer;
 
 import org.openstreetmap.gui.jmapviewer.AttributionSupport;
 import org.openstreetmap.gui.jmapviewer.MemoryTileCache;
@@ -136,7 +137,6 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
      * Initial zoom lvl is set to bestZoom
      */
     public int currentZoomLevel;
-    private boolean needRedraw;
 
     private final AttributionSupport attribution = new AttributionSupport();
     private final TileHolder clickedTileHolder = new TileHolder();
@@ -158,6 +158,11 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
     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() {
         @Override
         public void mouseClicked(MouseEvent e) {
@@ -263,10 +268,7 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
             tile.setImage(null);
         }
         tile.setLoaded(success);
-        needRedraw = true;
-        if (Main.map != null) {
-            Main.map.repaint(100);
-        }
+        invalidateLater();
         if (Main.isDebugEnabled()) {
             Main.debug("tileLoadingFinished() tile: " + tile + " success: " + success);
         }
@@ -296,14 +298,7 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
      * @see #invalidate() To trigger a repaint of all places where the layer is displayed.
      */
     protected void redraw() {
-        needRedraw = true;
-        if (isVisible()) Main.map.repaint();
-    }
-
-    @Override
-    public void invalidate() {
-        needRedraw = true;
-        super.invalidate();
+        invalidate();
     }
 
     /**
@@ -1029,14 +1024,30 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
     @Override
     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();
+            }
+        });
+    }
+
     private boolean imageLoaded(Image i) {
         if (i == null)
             return false;
@@ -1752,7 +1763,8 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
 
     @Override
     public boolean isChanged() {
-        return needRedraw;
+        // we use #invalidate()
+        return false;
     }
 
     /**
@@ -1898,8 +1910,6 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
         private void doPaint(MapViewGraphics graphics) {
             ProjectionBounds pb = graphics.getClipBounds().getProjectionBounds();
 
-            needRedraw = false; // TEMPORARY
-
             drawInViewArea(graphics.getDefaultGraphics(), graphics.getMapView(), pb);
         }
 
diff --git a/src/org/openstreetmap/josm/gui/layer/Layer.java b/src/org/openstreetmap/josm/gui/layer/Layer.java
index 7347110..f70f0d8 100644
--- a/src/org/openstreetmap/josm/gui/layer/Layer.java
+++ b/src/org/openstreetmap/josm/gui/layer/Layer.java
@@ -425,7 +425,11 @@ public abstract class Layer extends AbstractMapViewPaintable implements Destroya
      * Check changed status of layer
      *
      * @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;
     }
