Lambda表达式中的“在构造函数中启动新线程”警告(NetBeans IDE)

时间:2015-03-08 08:51:17

标签: java netbeans lambda thread-safety

当我在传统构造函数中启动新线程时,NetBeansIDE不会发出警告:

addActionListener(new ActionListener() {

    @Override
    public void actionPerformed(ActionEvent e) {

        (new SomeThread()).start();
    }
});

但是如果我将它转换为Lambda表达式,我会收到警告"在构造函数中启动新线程":

addActionListener((ActionEvent e) -> {

   (new SomeThread()).start();
});

这是什么问题?什么是正确的解决方案?

编辑1:

NetBeans IDE 8.0.2上的同样问题:

enter image description here

代码:

import java.awt.event.ActionEvent;
import javax.swing.Timer;

public class TimerClass extends Timer {

    public TimerClass() {
        super(1000, null);//Loop 1 sec

        addActionListener((ActionEvent e) -> {

            (new SomeClass()).start();
        });

    }

    private class SomeClass extends Thread {

        @Override
        public void run() {

        }
    }
}

1 个答案:

答案 0 :(得分:1)

这里的问题是,在构造函数中启动线程甚至注册侦听器被认为是危险的。

说明:
内部类包含对其封闭类的引用,这意味着您在构造函数中启动的线程在构造函数返回之前引用了TimerClass对象的状态 。因此,新线程可能会看到具有过时值的部分构造的对象(在一个线程中是当前值但在其他线程中不是当前值)。

一个简单的修复:
使构造函数变为私有,然后创建一个创建对象并启动线程的公共和静态工厂方法。

import java.awt.event.ActionEvent;
import javax.swing.Timer;

public class TimerClass extends Timer {

    // notice that the constructor is now private.
    private TimerClass() {
        super(1000, null);  //Loop 1 sec
    }

    // This will create the instance and then register the listener and start the thread
    public static TimerClass createInstance() {

        TimerClass instance = new TimerClass();       
        instance.addActionListener((ActionEvent e) -> {

            (instance.new SomeClass()).start();
        });

        return instance;
    }

    private class SomeClass extends Thread {

        @Override
        public void run() {

        }
    }
}

这样,线程将看到一个完全构造的对象,线程安全将被恢复(从而删除警告)。