Index: ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/BasicAction.java
===================================================================
--- ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/BasicAction.java	(revision 38216)
+++ ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/BasicAction.java	(working copy)
@@ -2013,7 +2013,7 @@
         {
             int numberOfThreads = ((pendingList != null) ? pendingList.size()
                     : 0);
-            Thread[] threads = new Thread[numberOfThreads];
+            AsyncPrepare[] threads = new AsyncPrepare[numberOfThreads];
             int i;
 
             /*
@@ -2027,47 +2027,19 @@
             }
 
             /*
-                * Now start the threads running.
-                */
-
-            for (i = 0; i < numberOfThreads; i++)
-            {
-                threads[i].start();
-                Thread.yield();
-            }
-
-            /*
-                * If one of these threads fails (PREPARE_NOTOK) do we terminate the
-                * others or simply let them finish? Currently we wait and let them
-                * all terminate regardless.
-                */
-
-            /*
                 * Now synchronise with the threads.
                 */
 
             for (int j = 0; j < numberOfThreads; j++)
             {
-                while (threads[j].isAlive())
-                {
-                    try
-                    {
-                        threads[j].join();
-                    }
-                    catch (Exception e)
-                    {
-                        tsLogger.logger.warn(e);
-
-                        p = TwoPhaseOutcome.PREPARE_NOTOK;
-                    }
-                }
-
                 /*
                      * Only set the outcome if the current value is PREPARE_OK.
                      */
 
+            	// Have to collect the result as that releases the thread
+            	int outcome = ((AsyncPrepare) threads[j]).outcome();
                 if (p == TwoPhaseOutcome.PREPARE_OK)
-                    p = ((AsyncPrepare) threads[j]).outcome();
+                    p = outcome;
 
                 threads[j] = null;
             }
Index: ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/AsyncPrepare.java
===================================================================
--- ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/AsyncPrepare.java	(revision 38216)
+++ ArjunaCore/arjuna/classes/com/arjuna/ats/arjuna/coordinator/AsyncPrepare.java	(working copy)
@@ -31,14 +31,16 @@
 
 package com.arjuna.ats.arjuna.coordinator;
 
+import java.util.ArrayList;
+import java.util.List;
 import com.arjuna.ats.internal.arjuna.thread.ThreadActionData;
 
 /**
- * Instances of this class are responsible for performing asynchronous
- * prepare on a specific AbstractRecord associated with a transaction.
- *
+ * Instances of this class are responsible for performing asynchronous prepare
+ * on a specific AbstractRecord associated with a transaction.
+ * 
  * @author Mark Little (mark@arjuna.com)
- * @version $Id: AsyncPrepare.java 2342 2006-03-30 13:06:17Z  $
+ * @version $Id: AsyncPrepare.java 2342 2006-03-30 13:06:17Z $
  * @since JTS 1.2.4.
  */
 
@@ -46,55 +48,116 @@
  * Default visibility.
  */
 
-class AsyncPrepare extends Thread
-{
+class AsyncPrepare extends Thread {
+	static List<AsyncPrepare> asyncPreparePool = new ArrayList<AsyncPrepare>();
+	static {
+		while (asyncPreparePool.size() < 100) {
+			asyncPreparePool.add(new AsyncPrepare());
+		}
+	}
 
-public static AsyncPrepare create (BasicAction act, boolean reportHeuristics,
-				   AbstractRecord rec)
-    {
-	return new AsyncPrepare(act, reportHeuristics, rec);
-    }
-    
-public void run ()
-    {
-	if (_theAction != null)
-	{
-	    /*
-	     * This is a transient thread so we don't
-	     * want to register it with the action it is
-	     * preparing, only change its notion of the
-	     * current transaction so that any abstract
-	     * records that need that information can still
-	     * have it.
-	     */
+	public static AsyncPrepare create(BasicAction act,
+			boolean reportHeuristics, AbstractRecord rec) {
+		synchronized (asyncPreparePool) {
+			while (asyncPreparePool.size() == 0) {
+				try {
+					asyncPreparePool.wait();
+				} catch (InterruptedException e) {
+					System.err
+							.println("BORKED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+					e.printStackTrace();
+					System.exit(-1);
+				}
+			}
+			AsyncPrepare asyncPrepare = asyncPreparePool.remove(0);
+			asyncPrepare.initialize(act, reportHeuristics, rec);
+			return asyncPrepare;
+		}
+	}
 
-	    ThreadActionData.pushAction(_theAction, false);
-	    
-	    _outcome = _theAction.doPrepare(_reportHeuristics, _theRecord);
+	public void run() {
+		while (true) {
+			synchronized (readyLock) {
+				while (!ready) {
+					try {
+						readyLock.wait();
+					} catch (InterruptedException e) {
+						System.err
+								.println("BORKED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+						e.printStackTrace();
+						System.exit(-1);
+					}
+				}
+				ready = false;
+				/*
+				 * This is a transient thread so we don't want to register it
+				 * with the action it is preparing, only change its notion of
+				 * the current transaction so that any abstract records that
+				 * need that information can still have it.
+				 */
 
-	    ThreadActionData.popAction(false);
+				ThreadActionData.pushAction(_theAction, false);
+
+				_outcome = _theAction.doPrepare(_reportHeuristics, _theRecord);
+
+				ThreadActionData.popAction(false);
+
+				_theRecord = null;
+				_theAction = null;
+
+				synchronized (resultLock) {
+					result = true;
+					resultLock.notify();
+				}
+			}
+		}
 	}
 
-	_theRecord = null;
-	_theAction = null;
-    }
+	public int outcome() {
+		synchronized (resultLock) {
+			while (!result) {
+				try {
+					resultLock.wait();
+				} catch (InterruptedException e) {
+					System.err
+							.println("BORKED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+					e.printStackTrace();
+					System.exit(-1);
+				}
+			}
+			int outcome = _outcome;
+			synchronized (asyncPreparePool) {
+				asyncPreparePool.add(this);
+				asyncPreparePool.notify();
+			}
+			result = false;
+			return outcome;
+		}
+	}
 
-public int outcome ()
-    {
-	return _outcome;
-    }
-    
-protected AsyncPrepare (BasicAction act, boolean reportHeuristics, AbstractRecord rec)
-    {
-	_theAction = act;
-	_outcome = TwoPhaseOutcome.PREPARE_NOTOK;
-	_reportHeuristics = reportHeuristics;
-	_theRecord = rec;
-    }
+	protected AsyncPrepare() {
+		start();
+	}
 
-private BasicAction    _theAction;
-private int            _outcome;
-private boolean        _reportHeuristics;
-private AbstractRecord _theRecord;
-    
+	private void initialize(BasicAction act, boolean reportHeuristics,
+			AbstractRecord rec) {
+		synchronized (readyLock) {
+			_theAction = act;
+			_outcome = TwoPhaseOutcome.PREPARE_NOTOK;
+			_reportHeuristics = reportHeuristics;
+			_theRecord = rec;
+			ready = true;
+			readyLock.notify();
+		}
+	}
+
+	private boolean ready;
+	private Object readyLock = new Object();
+	private boolean result;
+	private Object resultLock = new Object();
+	private BasicAction _theAction;
+	private int _outcome;
+	private boolean _reportHeuristics;
+	private AbstractRecord _theRecord;
+
 };
