Let's take a look at this code:
Import Java.util.bitSet; Import Java.util.CURRENT.COUNTDOWNLATCH; Public Class Anexample {Public Static Void Main (String [] ARGS) Throws pTion {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);}} ) ;start (); t2.start (); latch.countdown (); t1.join (); t2.Join (); // Crucial Part Here: System.out.println (bs.Get (1) );;:}}The question is, what is the result of this code output? What results can it output? Even on the collapsed JVM, what results are still allowed to print the output?
Let's take a look at what this program does:
Next, we need to construct some test cases to check these behaviors. Obviously, one of them can only run this example, and then observe the results and answer the questions above. However, answering the second result about allowable output, it requires some skills.
Carefulness can make a coincidence
Fortunately, we can use tools. JCSTRESS is a test tool for solving such problems.
We can easily write our test case as a form that JCSTRESS can recognize. In fact, it has prepared a variety of interfaces for us. We need an example. In this example, the two threads are executed together.
We use an actor2_arbiter1_teest <bitSet, Booleanresult2> interface. It will provide some method blocks and a conversion method for our two threads. This conversion method will represent the results of the BitSet state into a pair of Boolean values. We need to find a Java 8 JVM to run it, but now this is not a problem.
Look at the implementation below. Is it particularly simple?
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 News RN New BitSet ();} @OVERRIDE PUBLIC BOOLENRSULT2 NewResult () {Return New Booleanresult2 ();}}
Now when running this test, control will try all kinds of tricks to obtain all possible combinations of factors that drive these actions: parallel or non -merger, there are no load detection, and there are many many times in one line, many times, many times, many times, many times Therefore, all possible results will be recorded.
When you want to know how your parallel code is on, this is a better way to dig a hollow thought and think of all the details than yourself.
In addition, in order to use the comprehensive convenience brought by the JCSTRESS constraints, we need to provide it with an explanation of possible results. If so to do that, we need to use a simple XML file shown below.
<test name = "org.openjdk.jcstress.tests.custom.anexampletest"> <Contributed-By> Oleg Shelajev </Contributed-By> <descriptions if BitSet Works Well W ithout synchronization. </Description> <CASE> <Case> MATCH> [True, True] </Match> <EXPECT> Acceptable </Expect> <description> Seeing All Updates INTACT. </Case> <Match> [True, FAL se] </match> <Expect> Acceptable_interesting </Expect> <description> T2 Overwrites T1 Result. </Description> </Case> <Match> [FALSE, TRUE] </Match> <EXPECT > Acceptable_interesting </expect> <description> T1 OverWrites T2 Result. </Description> </Case> <nmatched> <EXPECT> Forbidden </exten> <description> All Other Cases are under unlenexpected. </Description On> </unually> </test>
Now, we are ready for this beast to start roaring. Through the use of the following command line to run test.
java -xx:+unlockdiagnosticvmoptions -xx:+Whiteboxapi -XX: -ResterictContended -JAR TESTS -CUSTOT/TARGET/JCSTRESS.JAR -T = ".*ANEXAMPLETEST"
The result we get is an elegant report.
It is clear that we can not only get the expected results, that is, both threads have set their positions, but also encounters a competitive condition, and one thread will cover the result of another thread.
Even if you see such a thing, you must have the calm mentality of "the mountain people have their own tricks", isn't it?
By the way, if you are thinking about how to modify this code, the answer is to carefully read the BitSet class in Javadoc and realize that it is not a thread security and requires external synchronization. This can easily be achieved by increasing the setting values of synchronous blocks.
synchronized (bs) {bs.set (1);}