Browse DevX
Sign up for e-mail newsletters from DevX


Use Callbacks to Isolate Concurrency Bugs : Page 2

By restricting all object data access to a callback mechanism, a Java server can contain all concurrency issues in a single place, making it much easier for you to see if concurrency constraints have been violated.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Benefits of Callbacks

Implementing a callback-based system requires extra work but it offers a number of benefits.

Normally, you must acquire certain locks before you can access certain data. Synchronized methods can make this mandatory, but they often aren't enough because the accessing code still has to obey some kind of synchronization protocol. As a system gets larger, you are more and more liable to access the data at the wrong time. Also, just putting synchronization blocks around everything becomes more likely to result in deadlock.

A callback, on the other hand, cannot access the data until that data is passed to the method, and it cannot access the data after the method is finished. Thus, the period in which the data is available is precisely delineated.

Furthermore, this mechanism is controlled by the gatekeeper, which can implement any kind of ordering mechanism. Conversely, the traditional wait/notify method provides no way of knowing which threads will gain access at which times.

(Of course, a callback method is free to squirrel away a pointer to the sensitive data, or pass it to another object or thread. This would certainly violate the linearity guarantee, but it is not the kind thing you are liable to do accidentally.)

The Sum Class

To initiate your design, create the sensitive data object first. Use a very simple data object called a Sum:

public class Sum { public int a, b, c; // ... }

A sum stores three numbers: a, b, and c, where c = a + b. However, these variables are public, which means someone could modify them so that c is no longer equal to a + b. This is the problem you want to avoid.

Of course, you easily could protect c by hiding it behind a synchronized access method, but as mentioned previously, this article's purpose is to explore an alternative method of data protection, one you would use when regular synchronization is either difficult or undesirable. Sum is a trivial object, but the complexity of the vulnerable data isn't the focus here. Rather, the vulnerability itself is.

The GateKeeper Class

All access to the Sum object will go through the GateKeeper class. A GateKeeper controls access to the object that is passed to its constructor:

GateKeeper gk = new GateKeeper( new Sum() );

To use the object hidden inside the GateKeeper, you must pass a callback to the GateKeeper's use() method:

gk.use( user );

The User, Accessor, and Mutator Interfaces

Java does not have first-class functions, so you can't use actual callbacks. However, you can get close to real callbacks by using interfaces.

The Accessor interface describes an object that wants to read a piece of data:

public interface Accessor extends User { public void access( Object o ); }

Similarly, the Mutator interface describes an object that wants to write to a piece of data:

public interface Mutator extends User { public void mutate( Object o ); }

You also need to define an empty interface called User. Objects that implement User are either Accessors or Mutators:

public interface User { }

For good measure, you also have the interface MutatingAccessor, which implements both Accessor and Mutator. This isn't strictly necessary, but it makes your object declarations neater:

public interface MutatingAccessor extends Accessor, Mutator { }

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date