Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why ArrayBlockingQueue constructor use ReentrantLock for visibility?

The code is from ArrayBlockingQueue,JAVA 8. The comment says:Lock only for visibility, not mutual exclusion.

    final Object[] items;
    int putIndex;
    int count;
    public ArrayBlockingQueue(int capacity, boolean fair,
                              Collection<? extends E> c) {
        this(capacity, fair);

        final ReentrantLock lock = this.lock;
        lock.lock(); // Lock only for visibility, not mutual exclusion
        try {
            int i = 0;
            try {
                for (E e : c) {
                    checkNotNull(e);
                    items[i++] = e;
                }
            } catch (ArrayIndexOutOfBoundsException ex) {
                throw new IllegalArgumentException();
            }
            count = i;
            putIndex = (i == capacity) ? 0 : i;
        } finally {
            lock.unlock();
        }
    }

I think the lock guarantees the visibility of count&putIndex. But why dont use volatile?

like image 402
Wendell Zahna Avatar asked Mar 15 '26 09:03

Wendell Zahna


1 Answers

The lock guarantees the visibility of all writes during: to count, to putIndex, and to the elements of items that it changes.

It doesn't need to guarantee mutual exclusion, as it is in the constructor and since the reference to this hasn't been given to other threads, there is no need for mutual exclusion (but it would guarantee that as well if the reference to this was given out before that point)

The comment is merely saying that the purpose of the lock is the visibility effects.

As to why you can't use volatile:

The methods that retrieve values from the queue, like poll, take and peek do need to lock for mutual exclusion. Making a variable volatile is not necessary; it could have an adverse performance impact.

It would also be hard to get it right because of the ordering: a volatile write happens before (JLS terminology) a volatile read on the same variable. That means that the constructor would have to write to the volatile variable as its last action, while all code that needs to be correctly synchronized needs to read that volatile variable first before doing anything else.

Locks are much easier to reason about and to get the ordering of accesses right, and, in this case - they are required in any case to execute multiple writes as one atomic action.

like image 113
Erwin Bolwidt Avatar answered Mar 16 '26 23:03

Erwin Bolwidt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!