Локальный AtomicReference и массив с лямбдой - PullRequest
3 голосов
/ 26 февраля 2020

Я пытаюсь изменить и получить локальную переменную с лямбда. Я знаю, что я должен использовать эффективно final для локальных переменных в лямбде. Когда я использую локальную переменную AtomicReference, изменение не удалось:

    public class Lamb {
    public static void main(String[] args) throws InterruptedException {
        Lamb lamb = new Lamb();
        GlobalL globalL = new GlobalL();
        lamb.a(globalL);
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                globalL.printSum();
            }).start();
        }
        Thread.sleep(3000);
        System.out.println("--------After Work--------");
        globalL.printSum();
    }
    public void a(GlobalL globalL) {
        AtomicReference<Integer> number = new AtomicReference<>(0);
        Work work = () -> {
            number.getAndSet(number.get() + 1);
            return number.get();
        };
        globalL.setWork(work);
    }
}
class GlobalL {
    private Work work;
    public void printSum() {
        System.out.println(work.getAndInc());
    }
    public void setWork(Work work) {
        this.work = work;
    }
}
interface Work {
    int getAndInc();
}

Вывод отличается каждый раз:

  1. -------- После работы ----- ---
    97
  2. -------- После работы --------
    99
    Когда я изменяю Atomi c на массив этого рабочего штрафа :
public void a(GlobalL globalL) {
        Integer[] number = {1};
        Work work = () -> {
            number[0]++;
            return number[0];
        };
        globalL.setWork(work);
}

Выводится каждый раз:
-------- После работы --------
102

  1. Что происходит с массивом и атомом c в этой ситуации?
  2. Как работать анонимным классом и labmda с неконечной локальной переменной?
  3. Как jvm работает с lamda?

1 Ответ

7 голосов
/ 26 февраля 2020

1) Код:

 number.getAndSet(number.get() + 1);
 return number.get();

является критическим разделом, поскольку существует пара операций, которые не выполняются атомарно. Вот почему вы получаете разные результаты. Чтобы исключить критический раздел:

public void a(GlobalL globalL) {
    AtomicInteger number = new AtomicInteger(0);
    Work work = () -> {
        return number.incrementAndGet();
    };
    globalL.setWork(work);
}

2) Вы не можете (см. этот или официальный учебник по анонимным классам )

3) ИМО, это должен быть отдельный вопрос. Короче говоря, лямбды - это просто синтаксис c сахара и они скомпилированы в анонимные внутренние классы.


Что касается вопроса why array works correctly?, то ответ: это не так t по той же причине: ++ не является атомом c оператором Чтобы доказать это, просто увеличьте число потоков, скажем до 1000:

   for (int i = 0; i < 1000; i++) {
        new Thread(() -> {
            globalL.printSum();
        }).start();
    }

Я получаю:

-------- После работы -------- 972

...