代码之家  ›  专栏  ›  技术社区  ›  L. Cornelius Dol

我可以从构造函数启动线程吗?

  •  2
  • L. Cornelius Dol  · 技术社区  · 17 年前

    考虑到J5+内存模型(JSR-133),以下代码线程是否安全且允许?

    如果安全的话,在某些情况下可以接受吗?

    public final class BackgroundProcessor
    extends Object
    implements Runnable
    {
    
    public BackgroundProcessor(...) {
        super();
    
        ...
    
        new Thread(this).start();
        }
    
    public void run() {
        ...
        }
    }
    

    当我阅读新的JMM规范时,启动线程会在与启动线程所做的任何事情建立关系之前创建一个事件。

    假设对象在构造函数中设置了私有成员变量,并在run()中使用。

    并且该类被标记为最终类,以防止子类意外。

    注意:这里有一个类似的问题,但这有一个不同的角度: calling thread.start() within its own constructor

    3 回复  |  直到 10 年前
        1
  •  2
  •   m.s.    10 年前

    除非你特别想在Java并发问题上不同意Brian Goetz的观点,否则我会认为这是错误的。尽管他的确切说法是“最好不要……”,但我还是会听从他的建议。从 JCIP,第42页:

    “A 常见错误 那可以让 “this”引用在 施工是为了 启动线程 来自构造函数 …… 没什么问题 创建 一根线 在构造函数中,但最好不要 到 开始 马上穿线。 相反,公开一个开始或初始化 方法……”

    更新: 只是想详细说明为什么这是一个问题。虽然在对thread.start()的调用和该线程实际开始执行的run()方法之间有一个内存屏障,但是在构造函数期间发生的事情之间没有这样的屏障。 之后 对thread.start()的调用。因此,如果还需要设置一些其他变量,或者如果JVM执行了一些“刚刚构造的对象”内务管理,那么这两个变量都不能保证被另一个线程看到:也就是说,另一个线程在不完整的状态下可以看到该对象。

    顺便说一句,除了它是否真的破坏了JMM之外,它还感觉有点像构造函数中的“奇怪的事情”。

        2
  •  3
  •   reevesy onejigtwojig    14 年前

    JLS: Threads and Locks

    两个动作可以由一个发生在关系之前的动作来命令。如果一个动作先于另一个动作发生,那么第一个动作对第二个动作可见并在第二个动作之前排序。

    如果我们有两个动作x和y,我们写hb(x,y)来表示x发生在y之前。

    • 如果x和y是同一条线的作用,x以程序顺序排在y之前,那么hb(x,y)。

    对线程的start()调用发生在已启动线程中的任何操作之前。

    因此,我们可以得出这样的结论:在调用thread.start()之前,构造函数中的所有操作都发生在启动线程中的所有操作之前。如果thread.start()在所有写入对象字段之后,则已启动的线程将看到所有写入对象字段的操作。如果对字段没有其他写入操作(其他线程将读取),则代码是线程安全的。

        3
  •  -3
  •   Roman    17 年前
    1. 您的类可能应该实现可运行的

    2. 是的,你的代码没问题

    3. 扩展对象是不必要的。我确信您知道对象是由所有类隐式扩展的。我不知道这种扩展对象的流行做法是从哪里来的,但它是一种糟糕的风格。