Java 为什么不';电话不是按顺序打吗?

Java 为什么不';电话不是按顺序打吗?,java,multithreading,synchronized,Java,Multithreading,Synchronized,我正在阅读一本书中有关线程/同步的一些简单示例,该书声称使用synchronized将允许在同一实例上调用一个线程来访问该方法。它确实按照承诺进行了序列化,但似乎在下面的Synchmain方法中创建的第三个调用方大约有9/10次出现在第二个调用方之前。此代码是示例代码,显示了没有同步方法的问题 class CallMe { void call(String msg) { System.out.print("[" + msg); try {

我正在阅读一本书中有关线程/同步的一些简单示例,该书声称使用
synchronized
将允许在同一实例上调用一个线程来访问该方法。它确实按照承诺进行了序列化,但似乎在下面的
Synch
main
方法中创建的第三个
调用方大约有9/10次出现在第二个调用方之前。此代码是示例代码,显示了没有同步方法的问题

class CallMe {
    void call(String msg) {
        System.out.print("[" + msg);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println("CallMe Interrupted");
        }
        System.out.println("]");
    }
}

class Caller implements Runnable {
    String msg;
    CallMe target;
    Thread t;

    public Caller (CallMe target, String msg) {
        this.target = target;
        this.msg = msg;
        t = new Thread(this);
        t.start();
    }

    @Override
    public void run() {
        target.call(msg);
    }
}

class Synch {
    public static void main(String args[]) {
        CallMe target = new CallMe();
        Caller c1 = new Caller(target, "Hello");
        Caller c2 = new Caller(target, "Synchronized");
        Caller c3 = new Caller(target, "World");

        try {       
            c1.t.join();
            c2.t.join();
            c3.t.join();        
        } catch (InterruptedException e) {
            System.out.println("Synch Interrupted");
        }
    }
}
这本书展示了两种处理这个问题的方法,它们是-
同步无效调用(字符串消息){…}

public void run(){synchronized(target){…}


很明显,这两个选项都有效,因为与原始代码相反,括号中的单词是一致的,如

[你好]
[World](大约90%的通话时间是向后的)
[已同步](1/许多已同步为第一条消息)

…原始代码没有押韵或理由。因此,我知道它正在“工作”,可以通过在每个
调用方
实例化上放置断点来直接看到它。显然,每次我这么做的时候,它都会起作用


为什么第三个
调用方
总是在第二个
调用方之前调用
调用方

线程按照定义是并行运行的,并且没有一个线程优先于任何其他线程

一旦所有线程都启动,首先运行的线程基本上是随机的,一般来说,第一个启动的线程会有一个轻微的“开头”,但与启动线程的开销相比,这个开头很小

您的特定环境的一个怪癖恰好是偏向于一个线程,结果可能会因不同的系统而有所不同,当然不应该依赖于此

顺便说一句,这是一种不好的做法,原因有很多:

public Caller (CallMe target, String msg) {
    this.target = target;
    this.msg = msg;
    t = new Thread(this);
    t.start();
}
(事实上,您可能收到了编译器警告)

更好的方法是提供一个启动方法

public Caller start() {
    t.start();
    return this;
}
然后呢

new Caller(target, msg).start();
这绝对可以确保调用方对象在线程开始处理它之前已完全初始化并准备就绪

为什么第三次呼叫总是在第二次呼叫之前呼叫

它并没有始终如一地这样做——它大约90%的时间都在这样做

基本上,同步不保证是先进先出的。。。而且也不能保证这些电话会按你期望的顺序打。三个新线程正在快速连续启动——无法保证哪个线程将首先开始执行其代码

从根本上说,如果您想对并行代码施加排序,您需要显式地这样做。同步不提供排序-它只提供排他性

它确实按照承诺进行了序列化,但在下面的Synch main方法中创建的第三个调用方似乎比第二个调用方早9/10倍

注意理解句子中“序列化”的含义:这意味着受同一个锁保护的所有代码段永远不会并行运行;换句话说,他们的执行将是连续的


这并不意味着“这些代码段的执行将以严格的、指定的顺序进行”。不会的。

谢谢,我很好奇,为什么这本书听起来如此真实?@BobbyDigital:这本书到底说了什么?它是说我们一定会以可预测的顺序得到结果,还是说我们不会得到交错输出?这些是非常不同的事情。“要修复前面的程序,您必须…要做到这一点,您只需要在
call()
s定义之前加上…”。你有一个很好的观点,它们不太具体,但确实说“程序的输出是…”,并且使用了“正确”的输出,但我认为它们不能保证任何东西。@BobbyDigital:听起来这两种方式都写得不好:(谢谢,我会接受你的建议。我认为示例代码只是为了说明这一点,我也不认为这不是一个非常有用的异常处理。当然,你能告诉我应该得到什么编译器警告吗?我没有得到警告?在Netbeans中,我得到“在构造函数中启动新线程”作为警告,还有另一个关于“在构造函数中导出此项”也可以得到。