The Atomic classes in Java 5: AtomicInteger and AtomicLong

The AtomicInteger class has a number of uses, but one is a drop-in replacement for an atomic counter. Before Java 5, we had to write classes with access to the counter variable in synchronized blocks or methods, or else use a volatile variable which is a lighter form of synchronization but with the risk that some updates could be missed if they happen concurrently. An AtomicInteger can be used as a drop-in replacement that provides the best of both worlds:

public class Counter {
  private AtomicInteger count = new AtomicInteger(0);
  public void incrementCount() {
    count.incrementAndGet();
  }
  public int getCount() {
    return count.get();
  }
}

The significant feature of AtomicInteger which we exploit is the call to incrementAndGet(). This method wraps round a machine instruction (or instructions) similar to CAS which will read, increment and set the underlying value in memory as a 'locked together' (atomic) action. Notice that this method returns the new incremented value, although we ignore it. On the other hand, if we were using AtomicInteger to do something like provide the next key to store in a database, we would probably want to take notice of the return value.

Unsurprisingly, AtomicLong provides similar atomic access to an underlying long variable.

Example: an atomic bitset to manage a database connection pool

In reality, our new Counter class is a bit pointless, because instead of an instance of Counter we may as well just use an instance of AtomicInteger as our counter! Let's consider a case where we might want to wrap additional functionality round an AtomicInteger (or AtomicLong).

Consider the case where we have a small list of resources, e.g. database connections, which we need to atomically "take" and "put back". If the number of available resources is smaller than 32, we can represent each one as a single bit of an AtomicInteger (or, with an AtomicLong, we could have up to 64). To take a resource from the pool, we need to atomically find the next unset bit and set it; to return the resource to the pool, we need to atomically unset the bit that we set before. We'll use an inner class to associate a Connection object with its position in the array. So our overall class will look like this:

public class ConnectionPool {
  public static class PooledConnection {
    private Connection conn;
    private int index;
    private PooledConnection(Connection conn, int index) { ... }
    public Connection getConnection() { return conn; }
  }

  private PooledConnection[] connections;
  private AtomicInteger usedConnections;

  public PooledConnection getConnection() {
    // ... find and return a connection whose bit in 'usedConnections' is not set
  }

  public void returnConnection(PooledConnection conn) {
    // ... look at conn.index and unset the corresponding bit in 'usedConnections'
  }
}

A client that needed a database connection would then do something like this:

PooledConnection pooledConn = pool.getConnection();
try {
  Connection c = pooledConn.getConnection();
  // run some SQL on c
} finally {
  pool.returnConnection(pooledConn);
}

Now, back to our ConnectionPool class. To 'get' a free connection, we need to atomically do the following:

To do all this atomically, our getConnection() implementation can look like the following example. We assume that we have a method firstUnsetBit(), which will find the index of the first bit that is 0 in a given integer, or return -1 if all bits are set. (We won't worry about the implementation of this method here, except to note that it is something that we can optimise: with an AND operation, we can test for several bits at once.)

public PooledConnection getConnection() {
  PooledConnection ret = null;
  do {
    int previousBits = usedConnections.get();
    int connectionNo = firstUnsetBit(previousBits);
    if (connectionNo == -1 || connectionNo >= NO_AVAILABLE_CONNECTIONS) {
      // If none currently available, just return null for now. Later, we'll
      // improve to wait for a connection to become available.
      return null;
    }
    int newBits = previousBits | (1 << connectionNo);
    if (usedConnections.compareAndSet(previousBits, newBits)) {
      ret = connections[connectionNo];
    }
  } while (ret == null);
  return ret;
}

The key call is to usedConnections.compareAndSet(). After we've found the first free bit and calculated what the new bit mask should be, we use this call to atomically check that the current value of the bit mask is still what we just read it to be (i.e. no other thread has just "snuck in" while we were find the free bit and calculating the new bit mask) and if so set the new value. In the unlikely event that another thread did sneak in, this method returns false and we loop round and try again until we succeeed, or until we find that there is no longer a free bit available (in which case we return null). The key thing is that we hold on to the CPU the whole time. We don't have to suspend our thread and delay for several milliseconds– as could have been the case if we'd used synchronized– just because another thread was simultaneously getting another connection.

The code to release a connection has similar logic. We read the current value of the usedConnections bit mask, clear the bit representing the connection we're returning, and then call compareAndSet. On the very unlikely occasion that this fails due to somebody sneaking in, we loop round until we're successful.

  public void returnConnection(PooledConnection conn) {
    int bitNo = conn.index;
    int previousBits, newBits;
    do {
      previousBits = usedConnections.get();
      // Clear the relevant bit
      newBits = previousBits & ~(1 << bitNo);
      // Try to set the new bit mask, and loop round until successful
    } while (!usedConnections.compareAndSet(previousBits, newBits));
  }

Note that our connection pool as it stands has limitations. If on a call to getConnection(), all connections are currently in use, our method simply returns null. In real life, we might want to wait (for some limited time) for a connection to become available. We'll see later that Java 5 provides the Semaphore class which can help us with this.

Next...

On the next page, we look at the AtomicReference class, which provides similar functionality as AtomicInteger and AtomicLong, but allows us to define a wrapper class to bind several variables together atomically.


If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.

Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.