这个双重锁定是不是破了吗?

时间:2014-11-18 22:37:18

标签: java multithreading concurrency synchronized

article解释了" Double-Checked Locking"其中的想法是减少锁争用。正如文章所解释的那样,它不起作用。请参阅表格中的代码示例"(仍然)断开多线程版本"双重检查锁定"成语"

现在我想我找到了一个应该有效的变体。问题是这是否正确。我们假设我们有一个消费者和一个通过共享队列交换数据的生产者:

class Producer {
     private Queue queue = ...;
     private AtomicInteger updateCount;

     public void add(Data data) {
         synchronized(updateCount) {
             queue.add(task);
             updateCount.incrementAndGet();
         }
     }
}

class Consumer {
    private AtomicInteger updateCount = new AtomicInteger(0);
    private int updateCountSnapshot = updateCount.get();

    public void run() {
        while(true) {
            // do something
            if(updateCountSnapshot != updateCount.get()) {
                // synchronizing on the same updateCount 
                // instance the Producer has
                synchronized(updateCount) { 
                    Data data = queue.poll()
                    //  mess with data
                    updateCountSnapshot = updateCount.get();
                }
            }
        }
    }
}

现在问的是你是否认为这种方法有效。我要求确定一下,因为如果没有那么多东西就会破坏......当updateCount在同一时间改变时,当只进入消费者中的同步块时,想法是减少锁争用

1 个答案:

答案 0 :(得分:3)

我怀疑你正在寻找更多Code Review

您应该考虑以下事项:

  • 这不是双重检查锁定。
  • 在没有数据到达的情况下,您的消费者将无所事事且吃cpu。
  • 您使用AtomicInteger作为信号量。
  • BlockingQueue将为您完成所有这些工作。
  • 您未正确确保共享updateCount
  • 您不必在原子上同步。

这是一个简单的制作人/消费者对,用于演示。

public class TwoThreads {

    public static void main(String args[]) throws InterruptedException {
        System.out.println("TwoThreads:Test");
        new TwoThreads().test();
    }

    // The end of the list.
    private static final Integer End = -1;

    static class Producer implements Runnable {

        final Queue<Integer> queue;

        public Producer(Queue<Integer> queue) {
            this.queue = queue;
        }

        @Override
        public void run() {
            try {
                for (int i = 0; i < 1000; i++) {
                    queue.add(i);
                    Thread.sleep(1);
                }
                // Finish the queue.
                queue.add(End);
            } catch (InterruptedException ex) {
                // Just exit.
            }
        }

    }

    static class Consumer implements Runnable {

        final Queue<Integer> queue;

        public Consumer(Queue<Integer> queue) {
            this.queue = queue;
        }

        @Override
        public void run() {
            boolean ended = false;
            while (!ended) {
                Integer i = queue.poll();
                if (i != null) {
                    ended = i == End;
                    System.out.println(i);
                }
            }
        }

    }

    public void test() throws InterruptedException {
        Queue<Integer> queue = new LinkedBlockingQueue<>();
        Thread pt = new Thread(new Producer(queue));
        Thread ct = new Thread(new Consumer(queue));
        // Start it all going.
        pt.start();
        ct.start();
        // Wait for it to finish.
        pt.join();
        ct.join();
    }

}