我正在使用双重检查锁定(DCL),以避免在不需要的情况下对对象进行同步。在我的情况下,我需要在某个缓冲区为空时进行同步,让“处理线程”等待“传递线程”再次通知它 - 否则,“处理线程”将在循环中运行而不做有用的事情。 / p>
两个线程共享这些对象:
Object bufferLock = new Object();
Queue<Command> commands = new ConcurrentLinkedQueue<>(); // thread safe!
线程1(“传递线程” - 填充缓冲区):
while (true)
Command command = readCommand();
commands.add(command);
synchronize (bufferLock){
bufferLock.notify(); // wake up Thread 2 (if waiting)
}
}
线程2(“处理线程” - 清空缓冲区):
while (true){
if (commands.peek() == null){ // not creating anything here
synchronized (bufferLock){
if (commands.peek() == null){ // also not creating anything
bufferLock.wait();
}
}
}
Command command = commands.poll();
processCommand(command);
}
现在,NetBeans正在出现关于DCL的警告,这使我更深入地研究了这个主题,因为我不知道DCL的概念 - 我刚开始自己使用它。
据我在互联网上阅读的几篇文章所理解,使用此模式时存在Java错误,但所有示例都将其与延迟加载结合使用。在这些示例中,在同步块中创建对象。在我的同步代码中,我不创建对象。
我的代码不安全吗? NetBeans在显示警告时是否正确?请注意,之前NetBeans与DCL有bug,所以我有些困惑。
答案 0 :(得分:1)
您创建的模式(或更确切地说,反模式!)并不严格构成Double Checked Locking,它通常是指对象引用从null开始然后由需要引用的第一个线程实例化的情况它,仅在空检查后同步。在Java 5之前,您无法严格地在Java中正确地实现这一点(尽管由于大多数JVM的实现方式,您可能会意外地使用它)。从Java 5开始,你可以使用它,但它基本上没有意义。 (你可能会对我之前写过的article on double-checked locking in Java感兴趣,它会更详细地讨论这个问题。类加载器实际上有内置的同步,用于实际需要像DCL这样的东西。)
现在,这就是所有类型的。你在这里所拥有的并不是严格意义上的DCL。
你遇到的问题是你试图混合范式。 Java并发库的 raison d'être通常是为了避免使用synchronized和wait / notify进行低级锁定。所以你真正要做的就是使用一些BlockingQueue并使用它的内置阻塞行为。同样,在其他示例中,我可能会向您推荐一些material I have written on blocking queues。
答案 1 :(得分:0)
请查看Brian Goetz撰写的“Java Concurrency in Practice”一书,第16.2.4节“双重检查锁定”。它解释了为什么做DCL是错误的事情。