双重检查锁定而不创建对象

时间:2013-07-31 14:34:59

标签: java multithreading synchronized

我正在使用双重检查锁定(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,所以我有些困惑。

2 个答案:

答案 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是错误的事情。

相关问题