Agent Lab is a framework for experimenting with simple non-interacting agents.
Here's a sample session consisting of three agents: Prime Generator, Pi Approximator, and Divider (performs integer division on a number until 0 is reached). The thread manager provides five commands (shown in red): g for go, s for suspend, r for resume, d for details (displays status of each thread), and h for halt.
-> g
ok
-> state = 37
next prime = 2
pi = 4.0
pi = 2.666666666666667
state = 12
state = 4
pi = 3.466666666666667
next prime = 3
pi = 2.8952380952380956
state = 1
state = 0
pi = 3.3396825396825403
state = 0
pi = 2.9760461760461765
pi = 3.2837384837384844
Divider stopped
next prime = 5
pi = 3.017071817071818
s
pi = 3.2523659347188767
ok
-> d
Pi Approximator (suspended)
Prime Generator (suspended)
Divider (stopped)
ok
-> r
ok
pi = 3.0418396189294032
-> pi = 3.232315809405594
pi = 3.058402765927333
pi = 3.2184027659273333
s
next prime = 7
ok
-> d
Pi Approximator (suspended)
Prime Generator (suspended)
Divider (stopped)
ok
-> h
ok
-> quit
bye
The output is a little confusing because all three threads and the manager are writing to the console window. Sometimes it requires multiple attempts to have suspend the threads.
The design follows the Master-Slave design pattern. The role of master is played by the manager, which is responsible for starting, stopping, suspending, and resuming its agents (slaves). Master extends Console, a reusable console user interface.

Each agent owns a thread that executes the agent's run method. Until it is stopped, the run method repeatedly calls the abstract update method, sleeps for one second (this releases the processor and gives other agents a chance to run), then waits to be resumed if it has been suspended:

Agents are suspended, resumed, and stopped by the manager, which runs in the master or user-interface thread.
Agent's implement Java's Runnable interface. This means an agent must be made the runner of a separately allocated thread:
Thread thread = new Thread(agent);
Starting the thread calls the agent's run method, which begins by trapping a pointer to its thread:
myThread = Thread.currentThread();
Here's the picture:

The run method repeatedly calls update, then causes its thread to sleep for one second. This releases control of the processor executing the thread, which gives other ready threads a chance to execute.
Stopping and suspending a thread is best done by the thread itself. To enable this we introduce two flags in the Agent class: suspended and stopped. Setting and getting these flags is done through synchronized setter and getter methods. These flags are checked by the agent's run method. If the stopped flag has been set (by the manager, for example), then the current iteration of the loop is allowed to complete before the run method exits (this stops the thread).
Recall that every object in Java can be used as a lock to synchronize access to some resource. It is common (although perhaps not advisable) to use the resource itself as a lock. In our case an agent serves as a lock for itself. In particular any thread must "hold" the agent in order to call the agent's suspend, stop, or resume methods. In addition to a queue of threads waiting to hold it, a lock also has a queue of threads waiting to be notified about some state change. A notified thread moves to the wait queue.
If the suspended flag has been set, then the checkSuspended method repeatedly calls the agent's inherited wait method. This suspends the agent's thread and places it on the agent's notification queue. The agent's resume method calls it's inherited notify method, which takes it off the queue and moves it to some processor's ready queue (of threads waiting to run). During this time the thread may get suspended again (or stopped), so we must repeatedly check these flags:
private synchronized
void checkSuspended() {
try {
while(!stopped &&
suspended) {
wait();
suspended = false;
}
} catch (InterruptedException e)
{
System.out.println(e);
}
}