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发生变化的同时仅在使用者中输入同步块时减少锁争用。

最佳答案

我怀疑您正在寻找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();
    }

}

07-24 15:42