我正在学习编写更好的多线程程序,线程安全和确定性.我遇到了这段代码
// 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]
解决方法:
我想这里有两个非常有趣的事情.
代码尝试依赖同步块来保持调用的顺序一致.这有两个问题:
1)同步块不公平(参见Synchronization vs Lock),因此首先获得锁定的同步块的线程可能不是第一个被授予访问该对象的块.然而,根据该帖子,方法级别的同步void synchronized run()将是一个公平的锁定(在Java 1.5中,而不是Java 1.6),因此等待锁定的第一个将是第一个授予对象的访问权限.
2)即使同步块是公平的,理论上第一个线程可能不是([synchronized])可能不是第一个在run()中调用某些代码的线程. [世界]实际上可以先称之为.