It's not a secret that parallel programs are easy to generate bugs. Writing this program is a challenge, and bugs that are silently generated during programming are not easily discovered. Many concurrent bugs can only be discovered or discovered by the user in system testing, functional testing. By then it would be costly to fix them-assuming they can be repaired-because they are so difficult to debug.
In this article, we introduce ConTest, a tool for testing, debugging, and measuring the scope of a parallel program. As you will soon see, ConTest is not a replacement for unit testing, but it is a complementary technique for dealing with unit test failures of parallel programs.
Why unit tests are not enough
When asked about any Java™ developer, they will tell you that unit testing is a good practice. Make the appropriate investment in the unit test and you will be rewarded later. With unit tests, bugs can be found earlier and can be repaired more easily than without unit tests. But common unit test methods, even when thoroughly tested, are not very effective in finding parallel bugs. That's why they can escape to the late stages of the program.
Why do unit tests often omit parallel bugs? The usual argument is that the problem with parallel programs (and bugs) is their uncertainty. But for unit testing purposes, the absurdity is that parallel programs are very certain. The following two examples explain this.
Non-modified Nameprinter
The first example is a class that does nothing but print a two-part name. For teaching purposes, we divide this task into three threads: one thread prints the name, one thread prints a space, one thread prints the last name and a new line. A mature synchronization protocol that includes synchronization of locks and invocation of Wait () and Notifyall () ensures that everything happens in the right order. As you can see in Listing 1, main () acts as a unit test, calling this class with the name "Washington Irving":
Listing 1. Nameprinter
public class Nameprinter {
private final String firstName;
private final String surName;
Private final Object lock = new Object ();
Private Boolean printedfirstname = false;
Private Boolean spacerequested = false;
public Nameprinter (String firstName, String surName) {
this.firstname = firstName;
this.surname = surName;
}
public void print () {
new Firstnameprinter (). Start ();
new Spaceprinter (). Start ();
new Surnameprinter (). Start ();
}
Private class Firstnameprinter extends Thread {
public void Run () {
try {
synchronized (lock) {
while (firstName = = null) {
lock.wait ();
}
System.out.print (firstName);
Printedfirstname = true;
Spacerequested = true;
Lock.notifyall ();
}
} catch (Interruptedexception e) {
assert (FALSE);
}
}
}
Private class Spaceprinter extends Thread {
public void Run () {
try {
synchronized (lock) {
while (! spacerequested) {
lock.wait ();
}
System.out.print (");
spacerequested = false;
Lock.notifyall ();
}
} catch (Interruptedexception e) {
assert (FALSE);
}
}
}
Private class Surnameprinter extends Thread {
public void Run () {
try {
synchronized (lock) {
while (! Printedfirstname | | spacerequested | | SurName = = null) {
lock.wait ();
}
System.out.println (surName);
}
} catch (Interruptedexception e) {
assert (FALSE);
}
}
}
public static void Main (string[] args) {
System.out.println ();
new Nameprinter ("Washington", "Irving"). Print ();
}
}