21

How would you answer the following question?

A method of a java class contains a block of code that must be executed atomically. Explain, using appropriate pseudo-code, how you would ensure that this block of code is executed atomically

Would I achieve this by making the method ..

public final AtomicInteger x = new AtomicInteger(0);

then ensuring the get statement returned:

x.get()

and if I wanted to increment the value of x would I do this?

x.getAndIncrement();
Leigh
  • 28,765
  • 10
  • 55
  • 103
germantom
  • 405
  • 2
  • 6
  • 15

2 Answers2

57

The answer depends on your definition of "atomic"

I know of three valid definitions for atomic:

  1. Atomic as in synchronized: only one thread can be executing the code at one time;
  2. Atomic as in ACID: all of the action/block happens, or none of it does;
  3. 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);
                }
            }
        }
    }
CPerkins
  • 8,968
  • 3
  • 34
  • 47
  • 1
    Interesting! I just see myself giving this as a test answer :P – Mifeet Jun 03 '13 at 22:01
  • 3
    About Atomic as in ACID I would add if (actionOneDone) actionTwoDone=... and the same for action three. Without that you must do reverse for all three actions. Also, if one action fails, you don't need to try others. As a whole, I like that very universal formulation. I am solving that task in the real life now and I thought up only much more specific solution. +1. – Gangnus May 05 '19 at 12:08
  • @Gangnus good addition. I was envisioning the doAction* methods as throwing exceptions when they fail, but that's not required, and I didn't say that. I'll edit to include your suggestion. – CPerkins May 06 '19 at 13:55
2

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.

Mifeet
  • 12,949
  • 5
  • 60
  • 108
  • 1
    That's very different from atomicity. Other functions can still observe the object in an inconsistent state. – SLaks Jun 03 '13 at 17:40
  • 1
    That's probably what the question was referring to, but a `synchronized` method in no way guarantees atomicity. – Sotirios Delimanolis Jun 03 '13 at 17:40
  • 1
    Yes, but the question obviously doesn't make sense, so I think it is just poorly expressed. I think they meant atomicity in the sense that interleaving of instructions in the "atomic" block is ruled out. I know it's not real atomicity, but they talk about "block of code" so they probably didn't even mean `AtomicInteger` or anything. – Mifeet Jun 03 '13 at 17:43
  • i think you're right about the AtomicInteger. if i instead make a reference to an object that will serve as a lock: 'private static Object x = new Object();' then use the following within the block to guard it. does that make sense? 'synchronized (x){}' – germantom Jun 03 '13 at 17:57
  • 2
    @germantom That would make that block within that method thread-safe, but any object within that block can still, possibly, be modified outside of it. So it really depends on atomicity in respect to something. – Sotirios Delimanolis Jun 03 '13 at 18:03