
ne of the most common errors Java programmers make when they first learn multi-threaded programming is to misunderstand locks. They believe that locking an object prevents access to its fields and methods, when in fact a lock on an object serves only to prevent other threads from gaining the same lock. This confusion, while understandable, can lead to vexing concurrency bugs.
In fact, many concurrency bugs come down to particular data being accessed at the wrong timeusually while someone else is changing it. Concurrency models in general rely on a set of interconnected synchronization zones, spread out over a number of source files. Such designs are vulnerable to "concurrency rot", in which the delicate relationships between different elements become hard to manage as code changes. Wouldn't it be nice if you really could lock an object and keep it all to yourself while you used it?
This article describes a method for constructing high-load concurrent servers, which prevents concurrency rot. By restricting all data access to a callback mechanism, the server can contain all concurrency issues in a single place, making it much easier to see if concurrency constraints have been violated.
Callbacks for Single-Threaded Access
Follow this tutorial to design and build a server that uses callbacks for single-threaded access. Objects containing sensitive data will be accessed
linearlythat is, they will be available to only one thread at a time.
The way you will enforce this is opposite from the way it is usually done. Normally, multiple threads attempt to gain access to a resource by competing for a lock. The thread that gets the lock has access until it releases the lock, at which point the next thread gets access.
In this design, however, you use a callback. You pass a callback object with a method called access()
to a gatekeeper. The gatekeeper passes the sensitive data to this method of the callback object. When the callback object is done using the data, the gatekeeper passes the data to the next callback object.
In a sense, this design isn't really different from the typical one: each object or thread gets a turn, and during its turn it has exclusive access. But instead of having the threads contend for control, the control is entirely in the hands of the gatekeeper object, which decides who gets access when.