在Lambda java 8中改变实例或本地对象变量

Nes*_*oli 17 java lambda java-8

我知道出于并发原因,我无法在Java 8中更新lambda中局部变量的值.所以这是非法的:

double d = 0;
orders.forEach( (o) -> {
     d+= o.getTotal(); 
});
Run Code Online (Sandbox Code Playgroud)

但是,如何更新实例变量或更改本地对象的状态?例如,Swing应用程序我有一个按钮和一个声明为实例变量的标签,当我点击按钮时我想要隐藏标签

 jButton1.addActionListener((  e) -> {
      jLabel.setVisible(false);
 });
Run Code Online (Sandbox Code Playgroud)

我没有编译器错误并且工作正常,但是...改变lambda中对象的状态是正确的吗?我将来会遇到并发问题还是坏事?

这是另一个例子.想象一下,下面的代码是在一个servlet的方法doGet中我会在这里遇到一些问题吗?如果答案是肯定的:为什么?

String key = request.getParameter("key");

Map<String, String> resultMap = new HashMap<>();  

Map<String, String> map = new HashMap<>();
//Load map

map.forEach((k, v) -> {
    if (k.equals(key)) {
        resultMap.put(k, v);
    }
});
 response.getWriter().print(resultMap); 
Run Code Online (Sandbox Code Playgroud)

我想知道的是:什么时候改变lambda中对象实例的状态是正确的?

ski*_*iwi 15

你的假设是不正确的.

你只能在lambdas中有效地改变最终变量,因为lambdas是匿名内部类的语法糖*.
*它们实际上不仅仅是语法糖,但这与此无关.

在匿名内部类中,您只能有效地更改最终变量,因此对于lambdas也是如此.

只要编译器允许,就可以使用lambdas做任何你想做的事情,现在就进入行为部分:

  • 如果您修改依赖于其他状态的状态,则在并行设置中,您将遇到麻烦.
  • 如果你修改依赖于其他状态的状态,在线性设置中,那么一切都很好.
  • 如果你修改了不依赖于其他任何东西的状态,那么一切都很好.

一些例子:

class MutableNonSafeInt {
    private int i = 0;

    public void increase() {
        i++;
    }

    public int get() {
        return i;
    }
}
Run Code Online (Sandbox Code Playgroud)
MutableNonSafeInt integer = new MutableNonSafeInt();
IntStream.range(0, 1000000)
        .forEach(i -> integer.increase());
System.out.println(integer.get());
Run Code Online (Sandbox Code Playgroud)

无论发生什么情况,这将按预期打印1000000,即使它取决于之前的状态.

现在让我们并行化流:

MutableNonSafeInt integer = new MutableNonSafeInt();
IntStream.range(0, 1000000)
        .parallel()
        .forEach(i -> integer.increase());
System.out.println(integer.get());
Run Code Online (Sandbox Code Playgroud)

现在它打印不同的整数,如199205或249165,因为其他线程并不总是看到不同线程所做的更改,因为没有同步.

但是说,我们现在摆脱了伪类和使用AtomicInteger,这是线程安全的,我们得到如下:

AtomicInteger integer = new AtomicInteger(0);
IntStream.range(0, 1000000)
        .parallel()
        .forEach(i -> integer.getAndIncrement());
System.out.println(integer.get());
Run Code Online (Sandbox Code Playgroud)

现在它再次正确打印1000000.
然而,同步成本很高,而且我们几乎失去了并行化的所有好处.


Mau*_*lin 5

一般来说:是的,您可能会遇到并发问题,但只会遇到已经存在的并发问题.Lambdafying它不会使代码非线程安全在以前,或反之亦然.在您给出的示例中,您的代码(可能)是线程安全的,因为ActionListener只在事件调度线程上调用它.如果您已观察到Swing单线程规则,则不会访问任何其他线程jLabel,如果是,则不会对其进行线程干扰.但这个问题与使用lambdas是正交的.