Вопрос об источнике CopyOnWriteArrayList # addIfAbsent, зачем снова нужен массив, нужен - PullRequest
3 голосов
/ 17 марта 2020

Я изучаю java параллельный пакет. После прочтения источника CopyOnWriteArrayLis у меня возникает следующий вопрос:

private boolean addIfAbsent(E e, Object[] snapshot) {
        final ReentrantLock lock = this.lock;
        // Here is my question.
        lock.lock();
        try {
            Object[] current = getArray();
            int len = current.length;
            if (snapshot != current) {
                // Optimize for lost race to another addXXX operation
                int common = Math.min(snapshot.length, len);
                for (int i = 0; i < common; i++)
                    if (current[i] != snapshot[i] && eq(e, current[i]))
                        return false;
                if (indexOf(e, current, common, len) >= 0)
                        return false;
            }
            Object[] newElements = Arrays.copyOf(current, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

Мой вопрос: зачем нужна оптимизация? Конечно, я гуглил себя, и ответ всегда - , чтобы исправить это, когда другие потоки могли добавлять новые элементы .

Но как объяснить lock.lock () ? Когда один поток получил блокировку, как другие потоки могут добавлять новые элементы?

Я знаю, может быть, это глупый вопрос, но я действительно смущаюсь по этому поводу.

1 Ответ

2 голосов
/ 28 марта 2020

Как вы, вероятно, видели, снимок сделан в этом методе

public boolean addIfAbsent(E e) {
        Object[] snapshot = getArray();
        return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
            addIfAbsent(e, snapshot);
    }

, который в конце вызывает метод, который вы задали в своем вопросе.

Итак, если есть манипуляции с массив между полученным снимком и блокировкой, блокируемой текущим потоком, они должны обрабатываться правильно.
Существуют различные способы, как такая манипуляция может происходить между этими двумя моментами времени, например, поток, который вызывает addIfAbsent метод прерывается планировщиком.
Другая, не столь маловероятная ситуация, если список часто записывается, будет состоять в том, что блокировка фактически блокируется другим потоком, когда текущий поток пытается заблокировать его, поэтому должен ждать, пока другой поток завершит свою работу (которая могла бы добавить элемент в список) и разблокировал блокировку, прежде чем он сможет заблокировать саму блокировку.

...