Java同步的奇怪行为(锁定、条件)

Java同步的奇怪行为(锁定、条件),java,multithreading,synchronization,Java,Multithreading,Synchronization,我只是想模拟一个简单的读者/作者场景 代码如下: import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.co

我只是想模拟一个简单的读者/作者场景

代码如下:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReadersWriters implements Solution {
    private final static Lock readerLock = new ReentrantLock();
    private final static Lock writerLock = new ReentrantLock();
    private final static Condition noReader = readerLock.newCondition();
    private static CountDownLatch countDown;
    private static volatile int readerCount=0;

    public static class Reader implements Runnable {
        private static int count=1;
        private int id = count++;

        @Override
        public void run() {
            int readCount = (int) (Math.random()*20);
            while (readCount > 0) {
                readCount--;
                writerLock.lock();
                try {
                    readerLock.lock();
                    try {
                        readerCount++;
                    } finally {
                        readerLock.unlock();
                    }
                } finally {
                    writerLock.unlock();
                }
                System.out.println("Reader "+id+" reading ("+readerCount+" readers)");
                try {
                    Thread.sleep((long) (Math.random()*500));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Reader "+id+" done");
                readerLock.lock();
                try {
                    readerCount--;
                    noReader.signalAll();
                } finally {
                    readerLock.unlock();
                }
                try {
                    Thread.sleep((long) (Math.random()*500));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            countDown.countDown();
        }
    }

    public static class Writer implements Runnable {
        private static int count=1;
        private int id = count++;

        @Override
        public void run() {
            int writeCount = (int) (Math.random()*20);
            while (writeCount>0) {
                writeCount--;
                writerLock.lock();
                try {
                    readerLock.lock();
                    try {
                        while (readerCount>0) {
                            noReader.await();
                        }
                        System.out.println("Writer "+id+" writing");
                        try {
                            Thread.sleep((long) (Math.random()*500));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("Writer "+id+" done");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        readerLock.unlock();
                    }
                } finally {
                    writerLock.unlock();
                }
                try {
                    Thread.sleep((long) (Math.random()*500));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            countDown.countDown();
        }
    }

    public static void main(String []args) {
        Executor exec = Executors.newCachedThreadPool();
        int numReaders = 10;
        int numWriters = 4;
        countDown = new CountDownLatch(numReaders+numWriters);
        for (int i=0; i<numReaders; i++) {
            exec.execute(new Reader());
        }
        for (int i=0; i<numWriters; i++) {
            exec.execute(new Writer());
        }
        try {
            countDown.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

我真的不明白这怎么会发生。。。只是控制台打印的内容弄混了,还是我真的遗漏了什么?

读取器中的
系统.out.println
调用不同步。到底是什么让你担心?

我相信你的问题在于这几行,对吗

Reader 9 reading (1 readers)
Reader 8 reading (3 readers)
Reader 5 reading (2 readers)
你希望它是1,2,然后是3个读卡器

简而言之,问题在于打印不是同步块的一部分

要根据您的代码给出可能原因的示例,请参见:

writerLock.lock();
try {
    readerLock.lock();
    try {
        readerCount++;
    } finally {
        readerLock.unlock();
    }
} finally {
    writerLock.unlock();
}
System.out.println("Reader "+id+" reading ("+readerCount+" readers)");
简而言之,对于上述代码段,
readerCount
的更新由
writerLock
保护。然而,有可能:

READER8                         READER5
(readerCount = 1 at this point)

lock writerLock
readerCount++  (=2)
unlock writerLock
                                lock writerLock               
                                update readerCount to 3
                                unlock writerLock

sysout of readerCount (3)
lock writerLock
readerCount-- (=2)

                                sysout of readerCount (2)
unlock writerLock
                                lock writerLock
                                readerCount-- (=1)
                                unlock writerLock
不难想象为什么这个数字看起来很奇怪


在readerCount++之后,将system out语句放在锁定的范围内,将给出预期的结果。

日志看起来不错:一个编写器,多个阅读器。问题是什么?对不起,问题是关于报告的无序读者的数量…读者没有被排序,他们只是被允许阅读是的,我想现在已经太晚了,我的大脑已经不能正常工作了…(我建议使用try with resource(form Java SE 7)让代码更清晰一些。)好吧,不太担心,但是好奇……:)即使打印没有同步,您的意思是一个人可能会读取readerCount的值,被“安排出去”让其他线程运行,然后才完成对println的调用?我想这是有道理的。。。。
READER8                         READER5
(readerCount = 1 at this point)

lock writerLock
readerCount++  (=2)
unlock writerLock
                                lock writerLock               
                                update readerCount to 3
                                unlock writerLock

sysout of readerCount (3)
lock writerLock
readerCount-- (=2)

                                sysout of readerCount (2)
unlock writerLock
                                lock writerLock
                                readerCount-- (=1)
                                unlock writerLock