Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Building cloud-ready, multicore-friendly applications, Part 1: Design principles

Prepare your applications for the future of distributed computing

  • Print
  • Feedback

Page 2 of 7

Don't lean on me

While atomic code may require other libraries, its execution is self-contained -- that is, it contains no call-level interdependencies. If there is interdependency across individual method calls, then your individual methods are not atomic, though they may form an atomic operation all together. Consider the simple example in Listing 1. A call on the setRadius() method must precede a call on any of the computational methods, such as getDiameter(). Therefore, setRadius() and getDiameter() are not atomic separately. Though this is fine from a purely object-oriented design standpoint, it has implications in distributed and parallel environments. The computational methods in Listing 1 cannot be processed in parallel across distributed workers or cores independent of setRadius().

Listing 1. A simple class with call-level interdependencies

package com.appistry.samples;

public class NonAtomicCircle {

    private int radius;

    public void setRadius(int radius) {
        this.radius = radius;
    }

    // setRadius must be called before any of the following

    public float getArea() {
        return 3.14f * (radius * radius);
    }

    public int getDiameter() {
        return 2 * radius;
    }

    public float getCircumference() {
        return 3.14f * getDiameter();
    }
}

The code in Listing 2, though more static in nature, does not have this dependency or its associated restrictions, and can be executed independently. In more complex classes involving equally complex state, this can become a critical design consideration.

Listing 2. A simple class with atomic methods

package com.appistry.samples;

public class AtomicCircle {

    private int radius;

    public void setRadius(int radius) {
        this.radius = radius;
    }

    // These computational methods can called independently
    // and distributed or run in parallel.

    public static float getArea(int radius) {
        return 3.14f * (radius * radius);
    }

    public static int getDiameter(int radius) {
        return 2 * radius;
    }

    public static float getCircumference(int radius) {
        return 3.14f * getDiameter(radius);
    }
}

It's simple, my dear Watson

Atomic code is also concise by its nature, and, as stated above, has a specific, clearly defined purpose. Fat, hairy, do-multiple-things methods like provisionLineItem() in Listing 3 are likely not atomic (though the methods it delegates to are candidates). If a method serves multiple purposes, then you should break those purposes up into separate atomic methods, and likely into separate classes, as reserveInventory() and calculateWeight() sound like separate concerns.

Listing 3. A non-atomic method with too many responsibilities

package com.appistry.samples;

public class OverAchiever {

    public void provisionLineItem(Shipment shipment, String sku, int quantity) {
        Product item = reserveInventory(sku, quantity);
        float weight = calculateWeight(item, quantity);
        shipment.add(item, quantity, weight);
        reportPopularityToCorporate(sku, quantity);
    }
    
}

The same holds true for long-running methods: they are not usually atomic. Long-running code may have a single purpose, but if you can break the method down into more atomic steps, then you gain flexibility when running that code in cloud environments or on multiple cores.

  • Print
  • Feedback

Resources