0

我正在尝试运行以下代码,但无法获得正确的输出。

预期输出“欢迎新程序员”

实际输出“新欢迎程序员”

class First {
    public synchronized void display(String msg) {
        System.out.print("["+msg);
        System.out.println("]");
    }
}

class Second {
    String msg;
    First fobj;
    Second (First fp,String str) {
        fobj = fp;
        msg = str;
        start();
    }

    public void run() {
        synchronized(fobj) {       //Synchronized block
            fobj.display(msg);
        }
    }
}

public class SyncroBlock {
    public static void main (String[] args) {
        First fnew = new First();
        Second ss = new Second(fnew, "welcome");
        Second ss1 = new Second(fnew,"new");
        Second ss2 = new Second(fnew, "programmer");
    }
}

我在这里做错了什么?有人可以纠正我吗?

4

2 回答 2

2
  1. 从构造函数启动线程是个坏主意。它违反了安全施工的原则。

    在构造过程中可能让 this 引用逃逸的一个常见错误是从构造函数启动线程。当一个对象从其构造函数创建一个线程时,它几乎总是与新线程共享它的 this 引用,或者显式(通过将其传递给构造函数)或隐式(因为ThreadorRunnable是拥有对象的内部类)。然后,新线程可能能够在完全构造之前看到拥有的对象。

    在构造函数中创建线程并没有错,但最好不要立即启动线程。相反,公开一个startinitialize方法来启动拥有的线程。从构造函数调用可覆盖的实例方法(既不是private也不是final)也可以允许this引用转义。

    3.2.1 安全构建实践,Java Concurrency in Practice by Brian Goetz

  2. Thread#start()调用可能需要一些时间,因此预期"welcome -> "new" -> "programmer"实际上可以是任何顺序

要按照您的计划进行,我们需要确保前一个run()已开始执行,然后再进行下一个。对于我的机器来说,两次100L通话之间的睡眠足以获得正确的顺序。

Second ss = new Second(fnew, "welcome");
Thread.sleep(100L);
Second ss1 = new Second(fnew,"new");
Thread.sleep(100L);
Second ss2 = new Second(fnew, "programmer");

这不是一个好技术,你不应该这样使用它。它使执行顺序化——我们不会从多线程中获得任何好处。

于 2018-07-16T07:47:23.957 回答
0

我认为您忘记实现 Callable 或 Runnable 或您的 Second class 所需的任何内容。

于 2018-07-16T09:43:28.420 回答