diff --git a/src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java b/src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java
index 47447f9..9a7b29a 100644
--- a/src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java
+++ b/src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java
@@ -9,11 +9,10 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executors;
+import java.util.function.Supplier;
 import java.util.regex.Pattern;
 
 import javax.imageio.ImageIO;
@@ -43,7 +42,7 @@
 public class BingAerialTileSource extends TMSTileSource {
 
     private static final String API_KEY = "Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU";
-    private static volatile Future<List<Attribution>> attributions; // volatile is required for getAttribution(), see below.
+    private static volatile List<Attribution> attributions; // volatile is required for getAttribution(), see below.
     private static String imageUrlTemplate;
     private static Integer imageryZoomMax;
     private static String[] subdomains;
@@ -52,12 +51,15 @@
     private static final Pattern quadkeyPattern = Pattern.compile("\\{quadkey\\}");
     private static final Pattern culturePattern = Pattern.compile("\\{culture\\}");
     private String brandLogoUri;
+    private ExecutorService executor;
 
     /**
      * Constructs a new {@code BingAerialTileSource}.
      */
     public BingAerialTileSource() {
         super(new TileSourceInfo("Bing Aerial Maps", null, null));
+        attributions = new ArrayList<>();
+        executor = Executors.newCachedThreadPool();
     }
 
     /**
@@ -66,6 +68,8 @@ public BingAerialTileSource() {
      */
     public BingAerialTileSource(TileSourceInfo info) {
         super(info);
+        attributions = new ArrayList<>();
+        executor = Executors.newCachedThreadPool();
     }
 
     protected static class Attribution {
@@ -79,7 +83,7 @@ public BingAerialTileSource(TileSourceInfo info) {
     @Override
     public String getTileUrl(int zoom, int tilex, int tiley) throws IOException {
         // make sure that attribution is loaded. otherwise subdomains is null.
-        if (getAttribution() == null)
+        if (attributions.isEmpty())
             throw new IOException("Attribution is not loaded yet");
 
         int t = (zoom + tilex + tiley) % subdomains.length;
@@ -234,46 +238,26 @@ public String getTermsOfUseURL() {
         return "http://opengeodata.org/microsoft-imagery-details";
     }
 
-    protected Callable<List<Attribution>> getAttributionLoaderCallable() {
-        return new Callable<List<Attribution>>() {
+    protected List<Attribution> getAttribution() {
+        final Supplier<List<Attribution>> loader = new Supplier<List<Attribution>>() {
 
             @Override
-            public List<Attribution> call() throws Exception {
-                int waitTimeSec = 1;
-                while (true) {
-                    try {
-                        InputSource xml = new InputSource(getAttributionUrl().openStream());
-                        List<Attribution> r = parseAttributionText(xml);
-                        return r;
-                    } catch (IOException ex) {
-                        System.err.println("Could not connect to Bing API. Will retry in " + waitTimeSec + " seconds.");
-                        Thread.sleep(TimeUnit.SECONDS.toMillis(waitTimeSec));
-                        waitTimeSec *= 2;
-                    }
+            public List<Attribution> get() {
+                try {
+                    InputSource xml = new InputSource(getAttributionUrl().openStream());
+                    List<Attribution> r = parseAttributionText(xml);
+                    return r;
+                } catch (IOException ex) {
+                    System.err.println("Could not connect to Bing API.");
                 }
+                return null;
             }
         };
-    }
-
-    protected List<Attribution> getAttribution() {
-        if (attributions == null) {
-            // see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
-            synchronized (BingAerialTileSource.class) {
-                if (attributions == null) {
-                  final FutureTask<List<Attribution>> loader = new FutureTask<>(getAttributionLoaderCallable());
-                  new Thread(loader, "bing-attribution-loader").start();
-                  attributions = loader;
-                }
-            }
-        }
-        try {
-            return attributions.get();
-        } catch (ExecutionException ex) {
-            throw new RuntimeException(ex.getCause());
-        } catch (InterruptedException ign) {
-            System.err.println("InterruptedException: " + ign.getMessage());
-        }
-        return null;
+        CompletableFuture.supplyAsync(loader, executor).thenAccept(attr -> {
+            attributions.clear();
+            attributions.addAll(attr);
+        });
+        return attributions;
     }
 
     @Override
