Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to atomically check TWO AtomicBooleans in Java in one safe operation without a synchronized block (i.e. low cost locks)?

So I have two AtomicBoolean and I need to check both of them. Something like that:

if (atomicBoolean1.get() == true && atomicBoolean2.get() == false) {

   // ...
}

But there is a race condition in between :(

Is there a way to combine two atomic boolean checks into a single one without using synchronization (i.e. synchronized blocks) ?

like image 444
JohnPristine Avatar asked Feb 21 '14 04:02

JohnPristine


People also ask

Is AtomicBoolean synchronized?

If all you're trying to do is make getting and setting a single boolean value atomic, then yes - you can use AtomicBoolean instead without any synchronization.

Is AtomicBoolean thread safe?

It's also possible to achieve thread-safety using the set of atomic classes that Java provides, including AtomicInteger, AtomicLong, AtomicBoolean and AtomicReference. Atomic classes allow us to perform atomic operations, which are thread-safe, without using synchronization.

How do you use AtomicBoolean in Java?

You can get the value of an AtomicBoolean using the get() method. Here is an example: AtomicBoolean atomicBoolean = new AtomicBoolean(true); boolean value = atomicBoolean. get();

What is difference between AtomicBoolean and boolean?

AtomicBoolean has methods that perform their compound operations atomically and without having to use a synchronized block. On the other hand, volatile boolean can only perform compound operations if done so within a synchronized block.


3 Answers

Well I can think of a couple ways but it depends on the functionality you need.

One way is to "cheat" and use an AtomicMarkableReference<Boolean>:

final AtomicMarkableReference<Boolean> twoBooleans = (
    new AtomicMarkableReference<Boolean>(true, false)
);

void somewhere() {
    boolean b0;
    boolean[] b1 = new boolean[1];
    b0 = twoBooleans.get(b1);

    b0 = false;
    b1[0] = true;

    twoBooleans.set(b0, b1);
}

But that's kind of a pain and only gets you two values.

So then you can use AtomicInteger with bit flags:

static final int FLAG0 = 1;
static final int FLAG1 = 1 << 1;

final AtomicInteger intFlags = new AtomicInteger(FLAG0);

void somewhere() {
    int flags = intFlags.get();

    int both = FLAG0 | FLAG1;
    if((flags & both) == FLAG0) { // if FLAG0 has a 1 and FLAG1 has a 0
        something();
    }

    flags &= ~FLAG0; // set FLAG0 to 0 (false)
    flags |=  FLAG1; // set FLAG1 to 1 (true)

    intFlags.set(flags);
}

Also kind of a pain but it gets you 32 values. You could probably create a wrapper class around this if you really wanted. For example:

public class AtomicBooleanArray {
    private final AtomicInteger intFlags = new AtomicInteger();

    public void get(boolean[] arr) {
        int flags = intFlags.get();

        int f = 1;
        for(int i = 0; i < 32; i++) {
            arr[i] = (flags & f) != 0;
            f <<= 1;
        }
    }

    public void set(boolean[] arr) {
        int flags = 0;

        int f = 1;
        for(int i = 0; i < 32; i++) {
            if(arr[i]) {
                flags |= f;
            }
            f <<= 1;
        }

        intFlags.set(flags);
    }

    public boolean get(int index) {
        return (intFlags.get() & (1 << index)) != 0;
    }

    public void set(int index, boolean b) {
        int f = 1 << index;

        int current, updated;
        do {
            current = intFlags.get();
            updated = b ? (current | f) : (current & ~f);
        } while(!intFlags.compareAndSet(current, updated));
    }
}

That's pretty good. Maybe a set is performed while the array is being copied in get but the point is you can get or set all 32 atomically. (The compare and set do-while loop is major ugly but it's how the atomic classes themselves work for things like getAndAdd.)

AtomicReference seems impractical here. It allows atomic gets and sets but once you have your hands on the internal object you are no longer updating atomically. You'd have to create a brand new object each time.

final AtomicReference<boolean[]> booleanRefs = (
    new AtomicReference<boolean[]>(new boolean[] { true, true })
);

void somewhere() {
    boolean[] refs = booleanRefs.get();

    refs[0] = false; // not atomic!!

    boolean[] copy = booleanRefs.get().clone(); // pretty safe
    copy[0] = false;
    booleanRefs.set(copy);
}

If you want to perform an interim operation on the data atomically (get -> change -> set, without interference) you have to use a lock or synchronization. Personally I would use a lock or synchronization since it's usually the case that the entire update is what you want to hold on to.

** UNSAFE !! **

Don't do this!

This can (possibly) be done with sun.misc.Unsafe. Here's a class that uses Unsafe to write to two halves of a volatile long, cowboy style.

public class UnsafeBooleanPair {
    private static final Unsafe UNSAFE;

    private static final long[] OFFS = new long[2];
    private static final long[] MASKS = new long[] {
        -1L >>> 32L, -1L << 32L
    };

    static {
        try {
            UNSAFE = getTheUnsafe();

            Field pair = UnsafeBooleanPair.class.getDeclaredField("pair");
            OFFS[0] = UNSAFE.objectFieldOffset(pair);
            OFFS[1] = OFFS[0] + 4L;

        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

    private volatile long pair;

    public void set(int ind, boolean val) {
        UNSAFE.putIntVolatile(this, OFFS[ind], val ? 1 : 0);
    }

    public boolean get(int ind) {
        return (pair & MASKS[ind]) != 0L;
    }

    public boolean[] get(boolean[] vals) {
        long p = pair;
        vals[0] = (p & MASKS[0]) != 0L;
        vals[1] = (p & MASKS[1]) != 0L;
        return vals;
    }

    private static Unsafe getTheUnsafe()
    throws Exception {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        return (Unsafe)theUnsafe.get(null);
    }
}

Importantly, the Javadoc in the Open JDK source for fieldOffset says not to do arithmetic with the offset. However, doing arithmetic with it appears to actually work in that I don't get garbage.

This nets a single volatile read for the entire word, but also (potentially) a volatile write to either half of it. Potentially putByteVolatile could be used to split a long in to 8 segments.

I wouldn't recommend that anybody use this (don't use this!) but it's kind of interesting as an oddity.

like image 60
Radiodef Avatar answered Oct 06 '22 20:10

Radiodef


Use a Lock:

 Lock l = ...;
 l.lock();
 try {
     // access the resource protected by this lock
 } finally {
     l.unlock();
 }

it's technically not a synchronized block, even though it's a form of synchronization, i think that what you're asking for is the very definition of synchronization, so i don't think is possible doing it 'without synchronization'.

like image 37
Camilo Avatar answered Oct 06 '22 19:10

Camilo


I can only think of two ways: use the lower two bits of an AtomicInteger or use a spinlock. I think Hotspot can optimize certain locks down to spinlocks on its own.

like image 21
David Ehrmann Avatar answered Oct 06 '22 19:10

David Ehrmann