Sunday, July 18, 2010

Thread Safety Rules

Safety Rules

Sometimes, a set of data is interdependent. For example, we might have two fields corresponding to a street address and a zip code. Changing an address might involve changing both of these fields. If the zip code is changed without a corresponding change to the street address, the data may be inconsistent or incoherent. Such a set of operations, which must be done as a unit -- i.e., either all of the operations are executed or none are -- in order to ensure consistency of the data, is called a transaction. The property of "doing all or none" is called atomicity. A system in which all transactions are atomic is transaction-safe.
The following rule suffices to ensure that your system is transaction-safe:
    All (potentially changeable) shared data is accessed only through the synchronized methods of a single object; no interdependent piece can be accessed independently.
Note that this means that shared data cannot be returned by these methods for access by other methods. If shared data is to be returned, a (non-shared) copy must be made. Further, if interdependent values are to be returned (i.e., a portion of the shared data is to be used by other methods), all of the relevant values must be returned in a single transaction.
For example, the address and zip code of the previous example should not be returned by two separate method calls if they are to be assumed consistent.
public class AddressData {
private String streetAddress;
private String zipCode;
public AddressData( String streetAddress, String zipCode) {
    this.setAddress( streetAddress, zipCode );
    ....
}
public synchronized void setAddress( String streetAddress,
                                     String zipCode) {
    // validity checks
    ....
    // set fields
    ....
}
public synchronized String getStreetAddress() { // problematic!
    return this.streetAddress;
}
public synchronized String getZipCode() { // problematic!
    return this.zipCode;
}
}
If this class definition were used, e.g. for
printMailingLabel( address.getStreetAddress(),
                   address.getZipCode() );
it would in principle be possible to get an inconsistent address. For example, between the calls to address.getStreetAddress() and address.getZipCode(), it is possible that a call to address.setAddress could occur. In this case, getStreetAddress would return the old street address, while getZipCode() would return the new zip code.
Instead, getStreetAddress() and getZipCode() should be replaced by a single synchronized method which returns a copy of the fields of the AddressData object:
public synchronized SimpleAddressData getAddress() {
    return new SimpleAddressData( this.streetAddress,
                                  this.zipCode );
}
The SimpleAddressData class can contain just public streetAddress and zipCode fields, without accessors. It is being used solely to return the two objects at the same time.

Introduction to Interactive Programming by Lynn Andrea Stein

No comments:

Post a Comment