diff --git a/src/org/openstreetmap/josm/data/osm/DataSet.java b/src/org/openstreetmap/josm/data/osm/DataSet.java
index eaad637..991703c 100644
--- a/src/org/openstreetmap/josm/data/osm/DataSet.java
+++ b/src/org/openstreetmap/josm/data/osm/DataSet.java
@@ -752,8 +752,7 @@ public final class DataSet implements Data, Cloneable, ProjectionChangeListener
             return;
 
         highlightedVirtualNodes = waySegments;
-        // can't use fireHighlightingChanged because it requires an OsmPrimitive
-        highlightUpdateCount++;
+        fireHighlightingChanged();
     }
 
     /**
@@ -765,8 +764,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 63e4bb4..eed786c 100644
--- a/src/org/openstreetmap/josm/gui/MapView.java
+++ b/src/org/openstreetmap/josm/gui/MapView.java
@@ -26,6 +26,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;
@@ -149,9 +150,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();
         }
 
@@ -159,7 +168,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);
             }
@@ -169,10 +178,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);
         }
 
         /**
@@ -185,6 +195,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;
+        }
     }
 
     /**
@@ -505,7 +526,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();
@@ -521,6 +541,8 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
      */
     private final HashMap<Layer, LayerPainter> registeredLayers = new HashMap<>();
 
+
+
     /**
      * Constructs a new {@code MapView}.
      * @param layerManager The layers to display.
@@ -843,8 +865,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++;
@@ -901,7 +924,6 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
         }
 
         nonChangedLayers.clear();
-        changedLayer = null;
         for (int i = 0; i < nonChangedLayersCount; i++) {
             nonChangedLayers.add(visibleLayers.get(i));
         }
@@ -1235,8 +1257,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 faf94af..a57bd9a 100644
--- a/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
+++ b/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
@@ -136,7 +136,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();
@@ -263,10 +262,8 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
             tile.setImage(null);
         }
         tile.setLoaded(success);
-        needRedraw = true;
-        if (Main.map != null) {
-            Main.map.repaint(100);
-        }
+        // TODO: Delay 100 ms
+        invalidate();
         if (Main.isDebugEnabled()) {
             Main.debug("tileLoadingFinished() tile: " + tile + " success: " + success);
         }
@@ -296,14 +293,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();
     }
 
     /**
@@ -1035,11 +1025,11 @@ 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);
+        // TODO: Trigger delayed invalidation if !done.
+        invalidate();
         return !done;
     }
 
@@ -1521,8 +1511,6 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
     public void paint(Graphics2D g, MapView mv, Bounds bounds) {
         ProjectionBounds pb = mv.getState().getViewArea().getProjectionBounds();
 
-        needRedraw = false;
-
         int zoom = currentZoomLevel;
         if (getDisplaySettings().isAutoZoom()) {
             zoom = getBestZoom();
@@ -1758,7 +1746,8 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener, FilterChangeLi
 
     @Override
     public boolean isChanged() {
-        return needRedraw;
+        // we use #invalidate()
+        return false;
     }
 
     /**
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;
     }
