Index: src/org/openstreetmap/josm/actions/JoinAreasAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/JoinAreasAction.java	(revision 13174)
+++ src/org/openstreetmap/josm/actions/JoinAreasAction.java	(working copy)
@@ -7,7 +7,9 @@
 
 import java.awt.event.ActionEvent;
 import java.awt.event.KeyEvent;
+import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -102,6 +104,7 @@
     public static class Multipolygon {
         private final Way outerWay;
         private final List<Way> innerWays;
+        private transient List<WayInPolygon> splitWayCache;
 
         /**
          * Constructs a new {@code Multipolygon}.
@@ -552,6 +555,7 @@
                     allWays.addAll(pol.innerWays);
                 }
                 if (ds != null) {
+
                     ds.setSelected(allWays);
                 }
             } else {
@@ -601,6 +605,13 @@
         // Dataset retrieving allows to call this code without relying on Main.getCurrentDataSet(), thus, on a mapview instance
         if (!areas.isEmpty()) {
             ds = areas.get(0).getOuterWay().getDataSet();
+
+            /* there is an EDT race without clearSelection(), if the join operation
+             * was started with more than one selected row (interval selection) in
+             * an open SelectionListDialog (triggers in old revision of JOSM as well,
+             * unrelated to further changes made below)
+             * .. needs to happen before cmds are commited to the redoundo stack */
+            ds.clearSelection();
         }
 
         boolean hasChanges = false;
@@ -644,21 +655,59 @@
         // Don't warn now, because it will really look corrupted
         boolean warnAboutRelations = !relations.isEmpty() && allStartingWays.size() > 1;
 
-        List<WayInPolygon> preparedWays = new ArrayList<>();
+        SimpleEntry<List<AssembledPolygon>, List<Way>> entry =
+        areas.stream().collect(ArrayList<LinkedList<Multipolygon>>::new,
+            (r, e) -> {
+                if (r.size() == 0) {
+                    r.add(new LinkedList<Multipolygon>());
+                    r.get(0).add(e);
+                } else {
+                    int i, j, li, lj;
+                    LinkedList<Multipolygon> tmp;
+                    for (j=0, lj=r.size(); j<lj; j++) {
+                        for (i=0, li=r.get(j).size(); i<li; i++) {
+                            tmp = new LinkedList<>();
+                            tmp.addAll(r.get(j));
+                            tmp.add(i, e);
+                            r.add(tmp);
+                        }
+                        r.get(j).add(i, e);
+                    }
+                }
+            },
+            (r1, r2) -> r1.addAll(r2))
+        .stream().map(
+            joinorder_variant -> {
+                List<WayInPolygon> preparedWays = new ArrayList<>();
+                final List<Way> discardedWays = new ArrayList<>();
+                final List<AssembledPolygon> boundaries = new ArrayList<>();
+                joinorder_variant.stream().forEachOrdered(
+                    mp -> {
+                        preparedWays.removeIf(t -> discardedWays.contains(t.way));
 
-        for (Way way : outerStartingWays) {
-            List<Way> splitWays = splitWayOnNodes(way, nodes);
-            preparedWays.addAll(markWayInsideSide(splitWays, false));
-        }
+                        synchronized (mp) {
+                            // splitWayOnNodes() changes geom without updating mp, but mp may be
+                            // accessed multiple times, so cache and use/reuse first time results
+                            if (mp.splitWayCache == null) {
+                                List<WayInPolygon> split = markWayInsideSide(splitWayOnNodes(mp.outerWay, nodes), false);
+                                for (Way way : mp.innerWays)
+                                    split.addAll(markWayInsideSide(splitWayOnNodes(way, nodes), true));
+                                mp.splitWayCache = split;
+                            }
+                        }
+                        preparedWays.addAll(mp.splitWayCache);
 
-        for (Way way : innerStartingWays) {
-            List<Way> splitWays = splitWayOnNodes(way, nodes);
-            preparedWays.addAll(markWayInsideSide(splitWays, true));
-        }
+                        // Find boundary ways
+                        boundaries.clear();
+                        boundaries.addAll(findBoundaryPolygons(preparedWays, discardedWays));
+                    }
+                );
+                return new SimpleEntry<>(boundaries, discardedWays);
+            })
+        .min((r1, r2) -> r1.getKey().size() - r2.getKey().size()).get();
 
-        // Find boundary ways
-        List<Way> discardedWays = new ArrayList<>();
-        List<AssembledPolygon> boundaries = findBoundaryPolygons(preparedWays, discardedWays);
+        List<Way> discardedWays = entry.getValue();
+        List<AssembledPolygon> boundaries = entry.getKey();
 
         //find polygons
         List<AssembledMultipolygon> preparedPolygons = findPolygons(boundaries);
@@ -1150,18 +1199,27 @@
      */
     public static List<AssembledPolygon> findBoundaryPolygons(Collection<WayInPolygon> multigonWays,
             List<Way> discardedResult) {
+        List<AssembledPolygon> result = new ArrayList<>();
+
         // In multigonWays collection, some way are just a point (i.e. way like nodeA-nodeA)
         // This seems to appear when is apply over invalid way like #9911 test-case
-        // Remove all of these way to make the next work.
         List<WayInPolygon> cleanMultigonWays = new ArrayList<>();
-        for (WayInPolygon way: multigonWays) {
-            if (way.way.getNodesCount() != 2 || !way.way.isClosed())
-                cleanMultigonWays.add(way);
-        }
+        multigonWays.stream().forEach(w -> {
+            if (w.way.isArea())
+                result.add(new AssembledPolygon(Arrays.asList(w)));
+            else if (w.way.getNodesCount()>1 && w.way.firstNode()!=w.way.lastNode() &&
+                // check that a duplicate or reversed duplicate of w has not already been added, fixes #10511
+                !cleanMultigonWays.stream().anyMatch(t -> {
+                    List<Node> l = w.way.getNodes();
+                    Collections.reverse(l);
+                    return t.way.getNodes().equals(w.way.getNodes()) || t.way.getNodes().equals(l);
+                }))
+                cleanMultigonWays.add(w);
+            else
+                discardedResult.add(w.way);
+        });
 
         WayTraverser traverser = new WayTraverser(cleanMultigonWays);
-        List<AssembledPolygon> result = new ArrayList<>();
-
         WayInPolygon startWay;
         while ((startWay = traverser.startNewWay()) != null) {
             List<WayInPolygon> path = new ArrayList<>();
@@ -1202,10 +1260,12 @@
                     // Inner loop -> remove
                     int index = path.indexOf(nextWay);
                     while (path.size() > index) {
-                        WayInPolygon currentWay = path.get(index);
-                        discardedResult.add(currentWay.way);
-                        traverser.removeWay(currentWay);
-                        path.remove(index);
+                        traverser.removeWay(path.get(index));
+                        traverser.setStartWay(path.get(index-1));
+                        if (traverser.walk() != null)
+                            discardedResult.add(path.remove(index).way);
+                        else
+                            index++;
                     }
                     traverser.setStartWay(path.get(index-1));
                 } else {
