为什么主要顺序中没有呼叫?

时间:2013-12-13 09:42:09

标签: java multithreading synchronized

我正在阅读一本关于线程/同步的简单示例,该书声称使用synchronized将允许在同一个实例上调用一个线程来访问该方法。它确实按照承诺进行了序列化,但似乎在下面的Caller 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");
        }
    }
}

这本书展示了处理这个问题的两种方法,它们是 -
synchronized void call(String msg) {...}
public void run() { synchronized (target) {...} }

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

  

[你好]
  [世界](大约90%的时间是呼叫倒退)
  [已同步](1 /多个已同步为第一个消息)

......原始代码没有押韵或理由。所以我知道它“正在工作”,可以通过在每个Caller实例上放置断点来直接看到它。当我这样做时,它显然对我来说很有效。

为什么第三个Caller在第二个call之前一直在调用{{1}}?

3 个答案:

答案 0 :(得分:4)

根据定义,线程并行运行,并且没有一个优先于任何其他线程。

一旦线程全部启动,它基本上是随机的,它将首先运行,一般来说,第一个启动将有一个轻微的“先发制人”,但与启动线程等的开销相比,先行开始很小。

你的特定环境的怪癖恰好偏向于一个线程,结果可能在不同的系统上有所不同,当然不应该依赖。

顺便提一下,这是不好的做法,原因有很多:

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();

这绝对可以确保Caller对象在Thread开始处理之前已完全初始化并准备就绪。

答案 1 :(得分:4)

  

为什么第三个呼叫在第二个呼叫之前一直呼叫呼叫?

它并没有这么做 - 它大约90%的时间都在这样做。

基本上,同步不能保证先进先出......并且无法保证呼叫甚至会按照您期望的顺序进行。三个新线程正在快速连续启动 - 没有保证哪个线程将首先实际开始执行其代码。

从根本上说,如果要对并行代码进行排序,则需要明确地这样做。同步不提供排序 - 它只提供排他性。

答案 2 :(得分:1)

  

它确实按照承诺进行了序列化,但似乎在下面的Synch主方法中创建的第三个Caller大约9/10倍出现在第二个之前。

小心理解句子中“序列化”的含义:它表示受同一锁保护的所有代码段永远不会并行运行;换句话说,它们的执行将是 serial

的含义是“执行这些代码段将以严格的指定顺序执行”。它不会。