Search⌘ K

Redesigning the Threading Logic

Explore redesigning threading logic in Java to facilitate unit testing with JUnit. Learn to separate application logic from threading concerns by restructuring code, using ExecutorService access, and overloading methods for stubbing. Develop tests that verify threading behavior efficiently while managing callbacks and profile processing.

We'll cover the following...

The bulk of the code in findMatchingProfiles() that remains after we extract collectMatchSets() and process() is threading logic. We could potentially go even one step further and create a generic method that spawns threads for each element in a collection, but let’s work with what we have now. Here’s the current state of the method:

Java
public void findMatchingProfiles(
Criteria criteria, MatchListener listener) {
ExecutorService executor =
Executors.newFixedThreadPool(DEFAULT_POOL_SIZE);
for (MatchSet set: collectMatchSets(criteria)) {
Runnable runnable = () -> process(listener, set);
executor.execute(runnable);
}
executor.shutdown();
}

Redesigning

Our idea for testing findMatchingProfiles() involves a little bit of redesign work. Here’s the reworked code:

Java
private ExecutorService executor =
Executors.newFixedThreadPool(DEFAULT_POOL_SIZE);
ExecutorService getExecutor() {
return executor;
}
public void findMatchingProfiles(
Criteria criteria,
MatchListener listener,
List<MatchSet> matchSets,
BiConsumer<MatchListener, MatchSet> processFunction) {
for (MatchSet set: matchSets) {
Runnable runnable = () -> processFunction.accept(listener, set);
executor.execute(runnable);
}
executor.shutdown();
}
public void findMatchingProfiles(
Criteria criteria, MatchListener listener) {
findMatchingProfiles(
criteria, listener, collectMatchSets(criteria), this::process);
}
void process(MatchListener listener, MatchSet set) {
if (set.matches())
listener.foundMatch(profiles.get(set.getProfileId()), set);
}

We need to access the ...