3

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?

2 Answers2

4

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.

Erwin Bolwidt
  • 30,799
  • 15
  • 56
  • 79
0

The lock guarantees all writes will be visible, including writes to the items array. Making the array volatile would not be enough to ensure writes to array elements are visible to all threads.

Joni
  • 108,737
  • 14
  • 143
  • 193