Let's take a look at this piece of code:
Import Java.util.BitSet;
Import Java.util.concurrent.CountDownLatch;
public class Anexample {public
static void Main (string[] args) throws Exception {
Bitset bs = new Bitset ();
Countdownlatch latch = new Countdownlatch (1);
thread T1 = new Thread (new Runnable () {public
void run () {
try {
latch.await ();
Thread.Sleep (1000);
} catch (Exception ex) {
}
bs.set (1);
}
});
Thread t2 = new Thread (new Runnable () {public
void run () {
try {
latch.await ();
Thread.Sleep (1000);
} catch (Exception e) {
}
Bs.set (2);
}
});
T1.start ();
T2.start ();
Latch.countdown ();
T1.join ();
T2.join ();
Crucial part:
System.out.println (Bs.get (1));
System.out.println (Bs.get (2));
}
The question is, what is the result of this code output? What exactly does it output, and what is the result of the above program, even on the crashed JVM, that still allows print output?
Let's take a look at what this program does:
- Initialization of a Bitset object
- Two threads running in parallel, setting the field value to true for the first and second digits respectively
- We tried to get these two threads running at the same time.
- Reads the value of the Bitset object, and then outputs the result.
Next, we need to construct some test cases to check these behaviors. Obviously, one of them can only run the example, then observe the results and answer the questions above, but it takes some skill to answer the second one about the result of allowing output.
Practice
Luckily, we can use the tools. Jcstress is a test tool that is created to solve such problems.
We can easily write our test case in a form that jcstress can recognize. In fact, it has prepared a number of possible interfaces for us. We need an example in this example where 2 threads execute concurrently and the result is represented as 2 Boolean values.
We use a actor2_arbiter1_test<bitset, booleanresult2> interface that will provide some method blocks and a conversion method for our 2 threads, This conversion method converts the result representing the Bitset state to a pair of Boolean values. We need to find a Java 8 JVM to run it, but now it's not a problem.
Look at the implementation below. Is it particularly concise?
public class Anexampletest implements
Actor2_arbiter1_test<bitset, booleanresult2> {
@Override
public void Actor1 (Bitset s, BooleanResult2 r) {
S.set (1);
}
@Override public
void Actor2 (Bitset s, BooleanResult2 r) {
S.set (2);
}
@Override public
void Arbiter1 (Bitset s, BooleanResult2 r) {
r.r1 = s.get (1);
R.R2 = S.get (2);
}
@Override public
Bitset newstate () {return
new Bitset ();
}
@Override public
BooleanResult2 Newresult () {return
new BooleanResult2 ();
}
}
Now, when running this test, the control will try all sorts of tricks to get all the possible combinations of factors that drive these actions: parallel or not parallel, with and without load detection, and many times in a row, so that all possible results are recorded.
When you want to know how your parallel code works, it's better than trying to figure out all the details on your own.
In addition, in order to take advantage of the comprehensiveness of the jcstress constraints, we need to give it an explanation of the possible results. To do that, we need to use a simple XML file like the one shown below.
<test name= "Org.openjdk.jcstress.tests.custom.AnExampleTest" > <contributed-by>oleg shelajev</
Contributed-by> <description> Tests If Bitset works, without synchronization.
</description> <case> <match>[true, true]</match> <expect>ACCEPTABLE</expect>
<description> seeing all updates intact. </description> </case> <case> <match>[true, false]</match> <expect>acceptabl
E_interesting</expect> <description> T2 overwrites T1 result. </description> </case> <case> <match>[false, true]</match> <expect>acceptabl
E_interesting</expect> <description> T1 overwrites T2 result.
</description> </case> <unmatched> <expect>FORBIDDEN</expect> <description>
All other cases are unexpected. </description> </unmatched&Gt
</test>
Now, we are ready to let the beast begin to roar. Run the test by using the following command line.
Java-xx:+unlockdiagnosticvmoptions-xx:+whiteboxapi-xx:-restrictcontended-jar tests-custom/target/jcstress.jar-t = ". *anexampletest"
And the result we've got is an elegant report.
It is now clear that not only do we get the expected result that two threads have set their bits, they also encounter a competitive condition in which one thread overwrites the results of another thread.
Even if you see this happening, there must be a "hermit's own ingenious plan," the calm mentality, is not it?
By the way, if you're thinking about how to modify this code, the answer is to read the Bitset class in Javadoc carefully and realize that it's not thread-safe and requires external synchronization. This can easily be achieved by increasing the synchronization block correlation settings.
Synchronized (BS) {
bs.set (1);
}