Java beans, therefore, are also deployed in such a dynamic, multithread environment, and herein lies the classic danger of encountering race conditions. Race conditions are timing-dependent program flow scenarios that can lead to state (program data) corruption. In the following section I will detail two such scenarios. Every Java bean needs to be designed with race conditions in mind so that a bean can withstand simultaneous use by several client threads.
Bean implementations have to assume that multiple threads are accessing and/or modifying a single bean instance at the same time. As an example of an improperly implemented bean (as it relates to multithreading awareness), consider the following BrokenProperties bean and its associated MTProperties test program:
BrokenProperties.java
import java.awt.Point;
// Demo Bean that does not guard against multiple thread use.
public class BrokenProperties extends Point {
//------------------------------------------------------------------- // set()/get() for 'Spot' property //-------------------------------------------------------------------
public void setSpot(Point point) { // 'spot' setter this.x = point.x; this.y = point.y;
} public Point getSpot() { // 'spot' getter return this; } } // End of Bean/Class BrokenProperties
MTProperties.java
import java.awt.Point; import utilities.*; import utilities.beans.*;
public class MTProperties extends Thread {
protected BrokenProperties myBean; // the target bean to bash..
protected int myID; // each thread carries a bit of ID
//------------------------------------------------------------------- // main() entry point //------------------------------------------------------------------- public static void main (String[] args) {
BrokenProperties bean; Thread thread;
bean = (BrokenProperties) BeansKit.newBean("BrokenProperties");
for (int i=0; i < 20; i++) { // start 20 threads to bash bean thread = new MTProperties(bean, i); // threads get access to bean thread.start(); } } //------------------------------------------------------------------- // MTProperties Constructor //-------------------------------------------------------------------
public MTProperties(BrokenProperties bean, int id) { this.myBean = bean; // note the bean to address this.myID = id; // note who we are } //------------------------------------------------------------------- // the thread main loop: // do forever // create new random Point with x == y // tell bean to adopt Point as its new 'spot' property // ask bean what its 'spot' property is now set to // throw a wobbly if spot x does not equal spot y //------------------------------------------------------------------- public void run() { int someInt; Point point = new Point();
while ( true ) { someInt = (int) (Math.random()*100); point.x = someInt; point.y = someInt; myBean.setSpot( point );
point = myBean.getSpot(); if ( point.x != point.y ) { System.out.println("Bean corrupted ! x= " + point.x +", y= " + point.y); System.exit(10); } System.out.print( (char) ('A' + myID) ); System.out.flush(); } } } // End of Class MTProperties
Note: The utilities package imported by MTProperties contains reusable classes and static methods developed for the book by the author.
The two source code listings above define a bean called BrokenProperties and the class MTProperties, which is used to exercise the bean from within 20 running threads. Let us follow MTProperties' main() entry point: First it instantiates a BrokenProperties bean, followed by the creation and starting of 20 threads. Class MTProperties extends java.lang.Thread, so all we need to do to turn class MTProperties into a thread is to override class Thread's run() method. The constructor for our threads takes two arguments: the bean object the thread will communicate with and a unique
identification, which allows the 20 threads to be easily differentiated at run-time.
The business end of this demo is our run() method in class MTProperties. Here we loop forever, creating random new (x,y) points, but with the following characteristic: their x coordinate always
equals their y coordinate. These random points are passed to the bean's setSpot() setter method and then immediately read back using the getSpot() getter method. You would expect the read spot property to be identical to the random point created some milliseconds ago. Here is a sample output of the program when invoked
at the command line:
ABBBBBBBBBBBBBBBBBBDJJJJJJJJJJJJJJJJJJJJEGHHHHHHHHHHHHHHHHHHSSSSSSSSSSSSSS IICCBBBBBBBBBBBBBBBBBKBDLJMBOPLQNRTPHHHHHHHHFFFFFFFFFFFFSSSSSSSSSSSSSSSSSS FFFFFFFFFFFFFFFAAAAAACCCCCCCKKKKKKKKKKKKKKKKKMMMMMMMMMMMMMMMMMMMMMMMMMMMDD JEOQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRBBBBBBBBBBBBBBBTTTTTTTTTTTTTTTTLPPPPPPP PPPPGGHHHFFFFFFFFIIIIIIIIIIIIIISSSSSSSSSSSSSSSSSSSSACCCCCCCCCCCCCCCCCCCKMD QQQQQQNNNNNNNNNNNNNNNNRRRRRTRRHHHHHHHHHFFFFFFFFFFFFFFFFFFIIIIIIIIIIIIIIIII MMMJEEEEEEEEEEDDDDEEEEEEEEEOOOOOOOOOOOOOOOOOOOOOOOOOOOQNNNNNNNNBTLPLRGFFFF FFFFFFFFFIIAAAAAAAAAAAAAAAAASSSSSSSSSSSSSSSSSSKKKKKKKKKKKKKKKKCCCCCCCMMJAA AACBean corrupted ! x = 67, y = 13 OOOOOOOOOOOOOOOOOOO
The output shows the 20 threads running in parallel (as far as a human observer goes); each thread uses the ID it received
at construction time to print one of the letters A to T, the first 20 letters of the alphabet. As soon as any thread discovers that the read back spot property does not conform to the programmed characteristic of x = y, the thread prints a "Bean corrupted" message and halts
the experiment.
What you are seeing is the state-corrupting side effect of a race condition within the bean's setSpot() code. Here is that method again:
public void setSpot(Point point) { // 'spot' setter
this.x = point.x;
this.y = point.y;
}
What could ever go wrong in such a simple bit of code? Imagine thread A calling setSpot() with a point argument equal to (67,67). If we now slow down the clock of the Universe so that we can see the Java virtual
machine (JVM) execute each Java statement, one at a time, we can imagine thread A executing the x coordinate copy statement (this.x = point.x;) and then, suddenly, thread A gets frozen by the operating system, and thread C is scheduled to run for a while. In its previous running state, thread C had just created its own new random point (13,13), called setSpot() itself, and then got frozen to make room for thread M, right after it had set the x coordinate to 13. So, the resumed thread C now carries on with its programmed logic: setting y to 13 and checking if the spot property is equal (13, 13), but finds
that it has mysteriously changed to an illegal state of (67, 13); the x coordinate being half the state of what thread A was setting spot to, and the y coordinate being half the state of what thread C had set spot to. The end result is that the BrokenProperties bean ends up with an internally inconsistent state: a broken property.