Index: trunk/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java	(revision 8325)
+++ trunk/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java	(revision 8326)
@@ -26,4 +26,5 @@
 import org.apache.commons.jcs.engine.behavior.ICacheElement;
 import org.openstreetmap.gui.jmapviewer.FeatureAdapter;
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.cache.ICachedLoaderListener.LoadResult;
 import org.openstreetmap.josm.data.preferences.IntegerProperty;
@@ -283,5 +284,5 @@
         } catch (Exception e) {
             log.log(Level.WARNING, "JCS - Error while loading object from cache: {0}; {1}", new Object[]{e.getMessage(), getUrl()});
-            log.log(Level.FINE, "Stacktrace", e);
+            Main.warn(e);
             for (ICachedLoaderListener l: listeners) {
                 l.loadingFinished(cacheData, LoadResult.FAILURE);
@@ -382,5 +383,6 @@
             return handleNotFound();
         } catch (Exception e) {
-            log.log(Level.WARNING, "JCS - Exception during download " + getUrl(), e);
+            log.log(Level.WARNING, "JCS - Exception during download {0}",  getUrl());
+            Main.warn(e);
         }
         log.log(Level.WARNING, "JCS - Silent failure during download: {0}", getUrl());
Index: trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java	(revision 8325)
+++ trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java	(revision 8326)
@@ -5,6 +5,9 @@
 import java.io.IOException;
 import java.net.URL;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Semaphore;
@@ -37,6 +40,9 @@
     private static final Logger log = FeatureAdapter.getLogger(TMSCachedTileLoaderJob.class.getCanonicalName());
     private Tile tile;
-    private TileLoaderListener listener;
     private volatile URL url;
+
+    // we need another deduplication of Tile Loader listeners, as for each submit, new TMSCachedTileLoaderJob was created
+    // that way, we reduce calls to tileLoadingFinished, and general CPU load due to surplus Map repaints
+    private static final ConcurrentMap<String,Set<TileLoaderListener>> inProgress = new ConcurrentHashMap<>();
 
     /**
@@ -133,5 +139,15 @@
         super(cache, connectTimeout, readTimeout, headers);
         this.tile = tile;
-        this.listener = listener;
+        if (listener != null) {
+            String deduplicationKey = getCacheKey();
+            synchronized (inProgress) {
+                Set<TileLoaderListener> newListeners = inProgress.get(deduplicationKey);
+                if (newListeners == null) {
+                    newListeners = new HashSet<>();
+                    inProgress.put(deduplicationKey, newListeners);
+                }
+                newListeners.add(listener);
+            }
+        }
     }
 
@@ -227,24 +243,35 @@
     @Override
     public void loadingFinished(CacheEntry object, LoadResult result) {
+        Set<TileLoaderListener> listeners;
+        synchronized (inProgress) {
+            listeners = inProgress.remove(getCacheKey());
+        }
+
         try {
-            tile.finishLoading(); // whatever happened set that loading has finished
-            switch(result){
-            case FAILURE:
-                tile.setError("Problem loading tile");
-                // no break intentional here
-            case SUCCESS:
-                handleNoTileAtZoom();
-                if (object != null) {
-                    byte[] content = object.getContent();
-                    if (content != null && content.length > 0) {
-                        tile.loadImage(new ByteArrayInputStream(content));
+            if(!tile.isLoaded()) { //if someone else already loaded tile, skip all the handling
+                tile.finishLoading(); // whatever happened set that loading has finished
+                switch(result){
+                case FAILURE:
+                    tile.setError("Problem loading tile");
+                    // no break intentional here
+                case SUCCESS:
+                    handleNoTileAtZoom();
+                    if (object != null) {
+                        byte[] content = object.getContent();
+                        if (content != null && content.length > 0) {
+                            tile.loadImage(new ByteArrayInputStream(content));
+                        }
                     }
-                }
-                // no break intentional here
-            case REJECTED:
-                // do nothing
-            }
-            if (listener != null) {
-                listener.tileLoadingFinished(tile, result.equals(LoadResult.SUCCESS));
+                    // no break intentional here
+                case REJECTED:
+                    // do nothing
+                }
+            }
+
+            // always check, if there is some listener interested in fact, that tile has finished loading
+            if (listeners != null) { // listeners might be null, if some other thread notified already about success
+                for(TileLoaderListener l: listeners) {
+                    l.tileLoadingFinished(tile, result.equals(LoadResult.SUCCESS));
+                }
             }
         } catch (IOException e) {
@@ -252,6 +279,8 @@
             tile.setError(e.getMessage());
             tile.setLoaded(false);
-            if (listener != null) {
-                listener.tileLoadingFinished(tile, false);
+            if (listeners != null) { // listeners might be null, if some other thread notified already about success
+                for(TileLoaderListener l: listeners) {
+                    l.tileLoadingFinished(tile, false);
+                }
             }
         }
