Local AtomicReference and array with lambda

Abdullajon :

I try change and get local variable with lambda. I know I should use effectively final for local variables in lambda. When I use AtomicReference local variable changing failed:

    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();
}

Output different every time:

  1. --------After Work--------
    97
  2. --------After Work--------
    99
    When I change Atomic to array this working fine:
public void a(GlobalL globalL) {
        Integer[] number = {1};
        Work work = () -> {
            number[0]++;
            return number[0];
        };
        globalL.setWork(work);
}

Output every time:
--------After Work--------
102

  1. What's going on with array and atomic this situation?
  2. How to work anonymous class and labmda with non final local variable?
  3. How jvm works with lamda?
Eugen Covaci :

1) The code:

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

is a critical section since there is a couple of operations that are not atomically performed. That's why you get different results. To eliminate the critical section:

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

2) You can't (see this or the official tutorial on Anonymous Classes)

3) IMO, it should be a separate question. To put it in few words, lambdas are just syntactic sugar and they get compiled into anonymous inner classes.


As for why array works correctly? question, the answer is: it doesn't for the same reason: ++ is not an atomic operator To prove it, just increase the number of threads to, let's say, 1000:

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

I'm getting:

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

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=8075&siteId=1