Index: src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java
===================================================================
--- src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java	(revision 15182)
+++ src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java	(working copy)
@@ -16,6 +16,9 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.RecursiveTask;
 
 import org.openstreetmap.josm.command.ChangeCommand;
 import org.openstreetmap.josm.command.Command;
@@ -38,6 +41,7 @@
 import org.openstreetmap.josm.tools.Geometry;
 import org.openstreetmap.josm.tools.Geometry.PolygonIntersection;
 import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Utils;
 
 /**
  * Checks if multipolygons are valid
@@ -44,7 +48,18 @@
  * @since 3669
  */
 public class MultipolygonTest extends Test {
+    private static final ForkJoinPool THREAD_POOL = newForkJoinPool();
 
+    private static ForkJoinPool newForkJoinPool() {
+        try {
+            return Utils.newForkJoinPool(
+                    "multipolygon_creation.numberOfThreads", "multipolygon-test-%d", Thread.NORM_PRIORITY);
+        } catch (SecurityException e) {
+            Logging.log(Logging.LEVEL_ERROR, "Unable to create new ForkJoinPool", e);
+            return null;
+        }
+    }
+
     /** Non-Way in multipolygon */
     public static final int WRONG_MEMBER_TYPE = 1601;
     /** No useful role for multipolygon member */
@@ -461,8 +476,7 @@
      * @param sharedNodes all nodes shared by multiple ways of this multipolygon
      */
     private void checkRoles(Relation r, List<PolyData> allPolygons, Map<Long, RelationMember> wayMap, Set<Node> sharedNodes) {
-        PolygonLevelFinder levelFinder = new PolygonLevelFinder(sharedNodes);
-        List<PolygonLevel> list = levelFinder.findOuterWays(allPolygons);
+        List<PolygonLevel> list = findOuterWaysMultiThread(allPolygons, sharedNodes);
         if (list == null || list.isEmpty()) {
             return;
         }
@@ -796,19 +810,44 @@
     }
 
     /**
+     * Collects outer way and corresponding inner ways from all rings.
+     * @param rings the polygon rings
+     * @param sharedNodes all nodes shared by multiple ways of this multipolygon
+     * @return list of nesting levels
+     */
+    private static List<PolygonLevel> findOuterWaysMultiThread(List<PolyData> rings, Set<Node> sharedNodes) {
+        PolygonLevelFinder worker = new PolygonLevelFinder(sharedNodes, rings, 0, rings.size(),
+                new ArrayList<PolygonLevel>(), 128);
+        if (THREAD_POOL != null) {
+            return THREAD_POOL.invoke(worker);
+        } else {
+            return worker.computeDirectly();
+        }
+    }
+
+    /**
      * Find nesting levels of polygons. Logic taken from class MultipolygonBuilder, uses different structures.
      */
-    private static class PolygonLevelFinder {
-        private final Set<Node> sharedNodes;
+    private static class PolygonLevelFinder extends RecursiveTask<List<PolygonLevel>> {
+        private final transient Set<Node> sharedNodes;
+        private final transient List<PolyData> input;
+        private final int from;
+        private final int to;
+        private final transient List<PolygonLevel> output;
+        private final int directExecutionTaskSize;
 
-        PolygonLevelFinder(Set<Node> sharedNodes) {
+        private static final long serialVersionUID = 0;
+
+        PolygonLevelFinder(Set<Node> sharedNodes, List<PolyData> input, int from, int to, List<PolygonLevel> output,
+                int directExecutionTaskSize) {
             this.sharedNodes = sharedNodes;
+            this.input = input;
+            this.from = from;
+            this.to = to;
+            this.output = output;
+            this.directExecutionTaskSize = directExecutionTaskSize;
         }
 
-        List<PolygonLevel> findOuterWays(List<PolyData> allPolygons) {
-            return findOuterWaysRecursive(0, allPolygons);
-        }
-
         private List<PolygonLevel> findOuterWaysRecursive(int level, List<PolyData> polygons) {
             final List<PolygonLevel> result = new ArrayList<>();
 
@@ -906,6 +945,34 @@
             }
             return null;
         }
+
+        @Override
+        protected List<PolygonLevel> compute() {
+            if (to - from <= directExecutionTaskSize) {
+                return computeDirectly();
+            } else {
+                final Collection<ForkJoinTask<List<PolygonLevel>>> tasks = new ArrayList<>();
+                for (int fromIndex = from; fromIndex < to; fromIndex += directExecutionTaskSize) {
+                    tasks.add(new PolygonLevelFinder(sharedNodes, input, fromIndex, Math.min(fromIndex + directExecutionTaskSize, to),
+                            new ArrayList<PolygonLevel>(), directExecutionTaskSize));
+                }
+                for (ForkJoinTask<List<PolygonLevel>> task : ForkJoinTask.invokeAll(tasks)) {
+                    List<PolygonLevel> res = task.join();
+                    if (res == null) {
+                        return null;
+                    }
+                    output.addAll(res);
+                }
+                return output;
+            }
+        }
+
+        List<PolygonLevel> computeDirectly() {
+            for (int i = from; i < to; i++) {
+                processOuterWay(0, input, output, input.get(i));
+            }
+            return output;
+        }
     }
 
     /**
