executing block of code atomically
I believe that the expected answer was something like this:
public class A {
public void foo() {
// .. some code
doSomething(); // the critical part
// .. come code
}
public synchronized void doSomething() { // this is a synchronized method
// the critical code
}
}
The execution of doSomething()
is not really atomic (Disclaimer: it's very different from atomicity), but the synchronized keyword ensures that only one thread can enter the execution of this method (on one instance of A
). I think that is what they meant instead of atomicity.
Here is another question about atomicity in Java. You may find something useful in there.
The answer depends on your definition of "atomic"
I know of three valid definitions for atomic
:
- Atomic as in synchronized: only one thread can be executing the code at one time;
- Atomic as in ACID: all of the action/block happens, or none of it does;
- Atomic as in uninterruptible: once the block starts, it can't be interrupted, even by task switching.
The first is probably what your professor meant, and it's pretty easy to accomplish (see below).
The second (atomic as in ACID) can be approximated. See below.
The third simply cannot be guaranteed in Java - it doesn't provide access to the "critical sections" primitives required for uninterruptibility. Fortunately, the need for this is pretty much restricted to operating systems and device drivers.
Atomic as in synchronized
This is relatively straightforward: simply enclose your block of code in a synchronized block. I've shown it as a discrete block below, but there are other options:
public void doSomethingQuasiAtomic() {
synchronized (exampleLock) {
// Your code block goes here.
// Only one thread will ever be in this block at a time.
...
}
}
Atomic as in ACID
There's no general-case solution for ACID
atomicity, but it can be approximated, also using synchronized code. In order to do this, each of the parts of the action must be safely reversible.
This is how I'd approach it:
For the sake of argument, assume there's a multipart action you need to do on an object we'll call exampleObj
, that you have three actions to be performed which can be safely reversed, and that all access to example
is synchronized on exampleLock
.
synchronized(exampleLock) {
boolean actionOneDone=false;
boolean actionTwoDone=false;
boolean actionThreeDone=false;
try {
actionOneDone=doActionOne(exampleObj); // or perhaps exampleObj.doActionOne();
if(actionOneDone) actionTwoDone=doActionTwo(exampleObj);
if(actionTwoDone) actionThreeDone=doActionThree(exampleObj);
} catch (Exception ex) {
// Whatever seems appropriate here.
} finally {
if (! (actionOneDone && actionTwoDone && actionThreeDone)) {
/* At least one part failed. Back out the completed actions in reverse order.
* Note that we never need to reverse action three since if it completed, so did the others.
*/
if (actionTwoDone) {
reverseActionTwo(exampleObj); // or perhaps exampleObj.reverseActionTwo();
}
if (actionOneDone) {
reverseActionOne(exampleObj);
}
}
}
}