Index: core/src/org/openstreetmap/josm/Main.java
===================================================================
--- core/src/org/openstreetmap/josm/Main.java	(revision 5694)
+++ core/src/org/openstreetmap/josm/Main.java	(working copy)
@@ -27,7 +27,6 @@
 import java.util.StringTokenizer;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 
 import javax.swing.Action;
@@ -374,14 +373,7 @@
             }
         });
 
-        try {
-            for (Future<Void> i : Executors.newFixedThreadPool(
-                    Runtime.getRuntime().availableProcessors()).invokeAll(tasks)) {
-                i.get();
-            }
-        } catch (Exception ex) {
-            throw new RuntimeException(ex);
-        }
+        Utils.runMultiThread(tasks);
 
         // hooks for the jmapviewer component
         FeatureAdapter.registerBrowserAdapter(new FeatureAdapter.BrowserAdapter() {
Index: core/src/org/openstreetmap/josm/actions/ValidateAction.java
===================================================================
--- core/src/org/openstreetmap/josm/actions/ValidateAction.java	(revision 5694)
+++ core/src/org/openstreetmap/josm/actions/ValidateAction.java	(working copy)
@@ -9,6 +9,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.concurrent.Callable;
 
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
@@ -21,6 +22,7 @@
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.io.OsmTransferException;
 import org.openstreetmap.josm.tools.Shortcut;
+import org.openstreetmap.josm.tools.Utils;
 import org.xml.sax.SAXException;
 
 /**
@@ -160,17 +162,31 @@
             errors = new ArrayList<TestError>(200);
             getProgressMonitor().setTicksCount(tests.size() * validatedPrimitives.size());
             int testCounter = 0;
-            for (Test test : tests) {
-                if (canceled)
-                    return;
-                testCounter++;
-                getProgressMonitor().setCustomText(tr("Test {0}/{1}: Starting {2}", testCounter, tests.size(),test.getName()));
-                test.setPartialSelection(formerValidatedPrimitives != null);
-                test.startTest(getProgressMonitor().createSubTaskMonitor(validatedPrimitives.size(), false));
-                test.visit(validatedPrimitives);
-                test.endTest();
-                errors.addAll(test.getErrors());
+            double start = System.currentTimeMillis();
+            List<Callable<List<TestError>>> tasks = new ArrayList<Callable<List<TestError>>>();
+            List<String> messages = new ArrayList<String>();
+            for (final Test test : tests) {
+                final String text = tr("Test {0}/{1}: Starting {2}", ++testCounter, tests.size(),test.getName());
+                messages.add(text);
+                tasks.add(new Callable<List<TestError>>() {
+                    @Override
+                    public List<TestError> call() throws Exception {
+                        if (canceled)
+                            return null;
+                        //getProgressMonitor().setCustomText(text);
+                        test.setPartialSelection(formerValidatedPrimitives != null);
+                        test.startTest(getProgressMonitor().createSubTaskMonitor(validatedPrimitives.size(), false));
+                        test.visit(validatedPrimitives);
+                        test.endTest();
+                        return test.getErrors();
+                    }
+                });
             }
+            for (List<TestError> result : Utils.runMultiThread(tasks/*, getProgressMonitor(), messages*/)) {
+                errors.addAll(result);
+            }
+            double end = System.currentTimeMillis();
+            System.out.println(end-start);
             tests = null;
             if (Main.pref.getBoolean(ValidatorPreference.PREF_USE_IGNORE, true)) {
                 getProgressMonitor().subTask(tr("Updating ignored errors ..."));
Index: core/src/org/openstreetmap/josm/tools/Utils.java
===================================================================
--- core/src/org/openstreetmap/josm/tools/Utils.java	(revision 5694)
+++ core/src/org/openstreetmap/josm/tools/Utils.java	(working copy)
@@ -26,8 +26,12 @@
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 
 import org.openstreetmap.josm.data.Version;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 
 /**
  * Basic utils, that can be useful in different parts of the program.
@@ -572,4 +576,43 @@
         }
         return connection;
     }
+    
+    /**
+     * Runs a list of tasks in parallel (with the same number of threads as the number of available processors) and
+     * waits for completion (success, cancellation or interruption).
+     * @param tasks The list of tasks to run
+     * @param monitor The progress monitor. May be null.
+     * @param messages The list of messages used to update the progress monitor. Must have the same size as tasks list. Ignored if monitor is null.
+     * @return The list of results
+     * @throws RuntimeException If any exception happens
+     * @since 5674
+     */
+    public static <T> List<T> runMultiThread(List<Callable<T>> tasks, ProgressMonitor monitor, List<String> messages) throws RuntimeException {
+        try {
+            List<T> results = new ArrayList<T>();
+            int i = 0;
+            for (Future<T> task : Executors.newFixedThreadPool(
+                    Runtime.getRuntime().availableProcessors()).invokeAll(tasks)) {
+                if (monitor != null && messages != null && messages.size() == tasks.size()) {
+                    monitor.setCustomText(messages.get(i++));
+                }
+                results.add(task.get());
+            }
+            return results;
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+    
+    /**
+     * Runs a list of tasks in parallel (with the same number of threads as the number of available processors) and
+     * waits for completion (success, cancellation or interruption).
+     * @param tasks The list of tasks to run
+     * @return The list of results
+     * @throws RuntimeException If any exception happens
+     * @since 5674
+     */
+    public static <T> List<T> runMultiThread(List<Callable<T>> tasks) throws RuntimeException {
+        return runMultiThread(tasks, null, null);
+    }
 }
