4

我正在学习编写更好的多线程程序,线程安全和确定性。我遇到了这段代码

// File Name : Callme.java
// This program uses a synchronized block.
  class Callme {
     void call(String msg) {
        System.out.print("[" + msg);
        try {
           Thread.sleep(1000);
        } catch (InterruptedException e) {
           System.out.println("Interrupted");
        }
        System.out.println("]");
     }
  }

  // File Name : Caller.java
  class Caller implements Runnable {
     String msg;
     Callme target;
     Thread t;
     public Caller(Callme targ, String s) {
        target = targ;
        msg = s;
        t = new Thread(this);
        t.start();
     }

     // synchronize calls to call()
     public void run() {
        synchronized(target) { // synchronized block
           target.call(msg);
        }
     }
  }
  // File Name : Synch.java
  public class Synch {
     public static void main(String args[]) {
        Callme target = new Callme();
        Caller ob1 = new Caller(target, "Hello");
        Caller ob2 = new Caller(target, "Synchronized");
        Caller ob3 = new Caller(target, "World");

        // wait for threads to end
        try {
           ob1.t.join();
           ob2.t.join();
           ob3.t.join();
        } catch(InterruptedException e) {
           System.out.println("Interrupted");
        }
     }
  }

产生以下输出(尝试了〜100次)

[Hello]
[World]
[Synchronized]

所以我的第一个问题是,这个输出是否有保证?我还观察到,如果我将睡眠更改为100它仍然会产生相同的输出,但是如果我将睡眠10更改为输出更改为

[Hello]
[Synchronized]
[World]

第二个问题是,如果有保证,为什么?最后但并非最不重要的,为什么这个输出?我希望它是

[Hello]
[Synchronized]
[World]
4

4 回答 4

3

我认为这里发生了两件非常有趣的事情。

代码尝试依赖同步块来保持调用顺序一致。这有两个问题:

1) 同步块是不公平的(请参阅Synchronization vs Lock),因此无论哪个线程首先到达锁定的同步块,都可能不是第一个被授予访问该对象的权限。然而,根据该帖子,方法级别的同步 public synchronized void run() 将是一个公平锁(在 Java 1.5 中,而不是 Java 1.6 中),因此第一个等待锁的将是第一个被授予对该对象的访问权限.

2) 即使同步块是公平的,理论上第一个线程也可能不是第一个调用 run() 中的某些代码的线程([synchronized])。[世界]实际上可以首先调用它。

于 2013-06-19T19:46:58.480 回答
2

不,不能保证输出。

于 2013-06-19T19:15:16.047 回答
1

没有输出顺序保证;输出是完全随机的,因为它是由操作系统决定的,哪个线程应该在一定时间内分配 CPU。将线程时间分配给 CPU 的算法是不确定的。

要使代码按此顺序打印 Hello, Synchronized, World 您应该更改Synch为:

Caller ob1 = new Caller(target, "Hello");
ob1.t.join();
Caller ob2 = new Caller(target, "Synchronized");
ob2.t.join();
Caller ob3 = new Caller(target, "World");
ob3.t.join();

也不需要synchronized块(以它的编写方式),因为只有一个线程会调用 run 方法;也msg只是读取,不写入会导致任何问题,并且call方法target不会target以任何方式改变状态。

于 2013-06-19T19:42:28.180 回答
1

不保证输出的顺序。该synchronized块防止不同的线程在打印它们的输出时交错,但它不能确保三个不同输出的任何顺序。不幸的是,您看到的“确定性外观”行为只是偶然。

于 2013-06-19T20:05:08.637 回答