我最近有a problem with two threads sticking in deadlock because they weren't monitoring the same object the way I thought they were。事实证明,实现Singleton模式solved the problem。 但为什么?
我只实例化了一个对象是私有属性的类的一个实例,所以我希望它无论如何都是有效的单例。
为了问题的完整性,这里还有一些说明差异的代码:
在实施Singleton模式之前:
class Worker {
private BlockingQueue q = new LinkedBlockingQueue();
public void consume(String s) {
// Called by thread 1.
// Waits until there is anything in the queue, then consumes it
}
public void produce(String s) {
// Called by thread 2.
// Puts an object in the queue.
}
// Actually implements Runnable, so there's a run() method here too...
}
线程是这样开始的:
Worker w = new Worker();
new Thread(w).start();
// Producer also implements Runnable. It calls produce on its worker.
Producer p = new Producer(w);
new Thread(p).start();
现在,当我检查produce()
和consume()
中实际使用的队列时,System.identityHashCode(q)
在不同的线程中给出了不同的结果。
使用单身人士模式:
class Worker {
private static BlockingQueue q;
private BlockingQueue getQueue() {
if(q == null) {
q = new LinkedBlockingQueue();
}
return q;
}
// The rest is unchanged...
}
突然间,它有效。为什么这种模式在这里是必要的?
答案 0 :(得分:4)
问题是您在new Worker()
构造函数中创建了Server
。你有这个:
public Server(Worker worker) {
this.clients = new ArrayList<ClientHandle>();
this.worker = new Worker(); // This is the problem.
// Don't do this in the Server constructor.
this.worker = new Worker();
// Instead do this:
this.worker = worker;
答案 1 :(得分:2)
根据您发布的伪代码,实际上并不是单例模式产生差异,而只是使用static
。在第一个示例中,队列未声明为静态,因此Worker
的每个实例都将实例化其自己的LinkedBlockingQueue
个实例。在第二个示例中声明static
时,队列级别创建并在所有实例之间共享。
根据您在其他问题中发布的代码,错误就在最后一行:
public Server(Worker worker) {
this.clients = new ArrayList<ClientHandle>();
this.worker = new Worker();
所以你的陈述
我只实例化了该对象所在类的一个实例 一个私有财产,所以我期望它有效地成为单身人士 反正。
不准确。你是在每个新服务器中实现一个新的工作者,而不是重用传入的服务器。