Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

JavaBeans: properties, events, and thread safety

Find out what measures you can take to ensure your beans survive in the real-life environment of a multithreaded application

  • Print
  • Feedback
Java is a dynamic language that includes easy-to-use multithreading language constructs and support classes. Many Java programs rely on multithreading to exploit internal application parallelism, improve networking performance, or speed up user feedback response. Most Java run-times use multithreading to implement Java's garbage collection feature. Finally, the AWT also relies on separate threads for its functioning. In short, even the simplest of Java programs are born in an actively multithreading environment.

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.

Multithreading issues with simple properties

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.

  • Print
  • Feedback

Resources
  • Read Laurence's review of "The Java Threads API makes it to print media" for a detailed review of one of the few books that delve into Java multithreading in depth /javaworld/jw-07-1997/jw-07-threads.html
  • Order your copy of Mastering JavaBeans directly from Sybex http://www.sybex.com/cgi-bin/bookpg.pl?2097back.html
  • Laurence is also the lead author of Mastering Java 1.1, also from Sybex http://www.sybex.com/cgi-bin/bookpg.pl?2070back.html