The intersection of technology and leadership

Controlling Time: Threaded Execution

Update (Feb 21): Thanks to some feedback from Taras and Lowell, the ThreadedRunner is the same as the java.util.concurrent.Executor class! I’ve updated the post to reflect this below. Thanks for the suggestion!

Unit testing threaded software can sometimes prove slightly cumbersome. Many of the examples on the net often show examples that tie a number of responsibilities together. It might be easy to see something like this:

  public class SomeExample {    
    public void doSomeWork() {
      Runnable runnable = new Runnable() {
        public void run() {
          // do some long running work
        }        
      };
      new Thread(runnable).start();
    }
  }

The thing about this class above is to realise what responsibilities it is mixing, including the new work they would like to (the item in Runnable), as well as how they would like to run it (the separate thread). It’s difficult to tease these two responsibilities apart the way it’s declared. The first thing I’d do is remove the dependency on the new thread. I’ll start by making use of the java.util.concurrent.Executor :

public interface Executor {
  void execute(Runnable runnable);
}
  
public class SingleThreadedExecutor implements Executor {
  public void execute(Runnable runnable) {
    runnable.run();
  }
}
public class NewThreadedExecutor implements Executor {
  public void execute(Runnable runnable) {
    new Thread(runnable).start();    	
  }
}

We’ll inject the SingleThreadedExecutor in our unit tests, so we only ever have one thread of execution, and we can rely on tests being consistently reproducible. We’ll inject the NewThreadedExecutor when we need to assemble our application for production. Here’s the step by step refactoring.

Wrap inside our new implementation

public class SomeExample {    
  public void doSomeWork() {
    Runnable runnable = new Runnable() {
      public void run() {
        // do some long running work
      }
    };
    new NewThreadedExecutor().start(runnable);
  }
}

Inject instance via the constructor

public class SomeExample {
  private final NewThreadedExecutor executor;
  public SomeExample(NewThreadedExecutor executor) {
    this.executor = executor;
  }  
  
  public void doSomeWork() {
    Runnable runnable = new Runnable() {
      public void run() {
        // do some long running work
      }
    };
    executor.start(runnable);
  }
}

Replace specific with interface

public class SomeExample {
  private final Executor executor;
  public SomeExample(Executor executor) {
    this.executor = executor;
  }  
  
  public void doSomeWork() {
    Runnable runnable = new Runnable() {
      public void run() {
        // do some long running work
      }
    };
    executor.start(runnable);
  }
}

In the tests, you can now inject an instance of SingleThreadedExecutor, whilst in production mode, you use the NewThreadedExecutor.

Disclaimers
Of course, this still leaves your testing strategy incomplete where you do want to test your software with the number of threads that you do expect, particularly when you need to share state across different processes.

4 Comments

  1. Taras Tielkes

    Executor?

  2. Lowell

    ThreadRunner already exists. It’s in the Java SE SDK and it’s called Executor.

  3. Nat Pryce

    You have to be careful when using your SingleThreadedExecutor in unit tests. It has different behaviour w.r.t. reentrancy than a separate thread (assuming the object under test implements locking correctly).

    A task on another thread would be locked out of the object until the method that spawned it had returned and left the object in a stable, valid state.

    The SingleThreadedExecutor calls back immediately on the same thread to the task that the object has spawned. This means that the task can reenter the object because the current thread already owns the object’s lock. The task callback therefore sees the state of the object *before* the method that spawned it has completed. Depending on the object’s implementation, this can allow the task to see the object in an invalid state.

    The solution is to capture the callbacks and then execute them explicitly in the test. The JMock library provides implementations of Executor and ScheduledExecutorService that work this way. You can use them on their own without any of the rest of jMock. E.g. if you prefer EasyMock for some reason you can use it in combination with jMock’s concurrency classes.

    For more information see http://www.jmock.org/threads.html

  4. Patrick

    Hi Nat,

    Thanks for your comment and I agree with you that you do have to be careful about using the SingleThreadedExecutor. Thanks for the link. I’ll definitely take a look at using it.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

© 2022 patkua@work

Theme by Anders NorenUp ↑