具有 lambda 的本地 AtomicReference 和数组

Local AtomicReference and array with lambda

我尝试使用 lambda 更改并获取局部变量。我知道我应该对 lambda 中的局部变量使用 effectively 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
    当我将 Atomic 更改为数组时,这个工作正常:
public void a(GlobalL globalL) {
        Integer[] number = {1};
        Work work = () -> {
            number[0]++;
            return number[0];
        };
        globalL.setWork(work);
}

每次输出:
--------下班后--------
102

  1. array和atomic这种情况是怎么回事?
  2. 如何使用非 final 局部变量的匿名 class 和 labmda?
  3. jvm 如何与 lamda 一起工作?

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) 你不能(参见 or the official tutorial on Anonymous Classes

3) IMO,这应该是一个单独的问题。简而言之,lambdas 只是语法糖,它们被编译成匿名内部 类.


至于why array works correctly?问题,答案是:不会 同理:++ is not an atomic operator 为了证明这一点,只需将线程数增加到 1000:

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

我得到:

--------After Work-------- 972