【问题标题】:Concurrent readers-writers behaving in an unexpected way并发读者-作者以意想不到的方式表现
【发布时间】:2014-01-19 21:26:04
【问题描述】:

我正在尝试制作一个运行多个并行读取器和写入器的程序。我使用ExecutorService 类在循环中启动一些线程,Writer 类的run() 方法只调用两个方法StartWrite()EndWrite() 以及Reader 类StartRead()EndRead()。这些方法封装在监视器中。

这是监视器:

public class RWMonitorAN {
  static int readers = 0;     
  static boolean writing = false;
  public static RandomAccessFile f;
  public static int n = 0;
  private final ReentrantLock mylock = new ReentrantLock();
  private final Condition toWrite = mylock.newCondition();
  private final Condition toRead = mylock.newCondition();

  public RWMonitorAN()
  {
    try { f = new RandomAccessFile("datos.dat", "rw"); } catch (IOException e) {}
  }

  void StartRead() {
    mylock.lock();
    try
    {
        if (writing)
            try {
                 toRead.wait();
                 toWrite.wait();
              } catch (InterruptedException e) {}

        readers++;

        try
        {   
            f.seek(0);
            while (f.getFilePointer()<f.length())
                System.out.print(f.readInt()+" ");
            System.out.println();
        } catch (IOException e) { e.printStackTrace(); }
    }
    finally { mylock.unlock(); }
  }

  void EndRead() {
    mylock.lock();
    try
    {
        readers--;
        if (readers == 0) 
            toWrite.signal();
    }
    finally { mylock.unlock(); }
  }

  void StartWrite() {
    mylock.lock();
    try
    {
        if (writing || readers != 0)
          try {
             toWrite.wait();
          } catch (InterruptedException e) {}

        writing = true;

        try 
        { 
          f.writeInt(n); 
        } catch(IOException e) {}

        n++;
    }
    finally { mylock.unlock(); }
  }

  void EndWrite() {
    mylock.lock();
    try
    {
        writing = false;

        toWrite.signal();
    toRead.signal();
    }
    finally { mylock.unlock(); }
  }

但是有一个问题:程序在写入和读取预期数量的数据之前就结束了。例如,如果我启动 20 个写入器和 20 个读取器线程,我希望写入和读取 20 个数字,但只有 10~ 是。如果我再次运行程序(datos.dat 已经创建),它会继续写入和读取,然后写入 20 个数字。

测试:

第一次运行

  • 写在最后执行的线程中:0 1 2 3 4
  • 读入最后执行的线程:0 1 2 3 4

第二次运行

  • 写在最后执行的线程中:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
  • 读入最后执行的线程:0 1 2 3 4 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

怎么了?我怎样才能让它正常工作?

编辑:我尝试在每次读者完成阅读时打印读者数量,并且多次打印相同数量的读者。这不应该发生吗? readers-- 在代码的受锁保护部分执行。

【问题讨论】:

  • 您能否确保它确实在运行,toWrite.wait()toRead.wait() 会在此处抛出 IllegalMonitorStateException,除非您没有向我们展示更多代码。
  • 检查我的答案的结尾,关于您的编辑。
  • 你是对的,那些异常被抛出了,我捕捉到了错误的异常,所以似乎没有抛出任何异常。
  • @dabadaba 我推荐的两件事是 1. 使用 await 而不是 wait 2. 让它坐在 while(writing || reading) 而不是 if
  • 我认为在使用类 Condition 时不需要使用,而 ifs 工作得一样好,但效率更高。

标签: java multithreading file concurrency monitor


【解决方案1】:

您在任何地方都捕捉到您的异常,而不是在任何地方报告它们,也没有做任何其他事情来为它们服务。 “让它正常工作”的第一件事可能是检查是否有异常被调用。例如,如果您在 Java 中对一个数据结构使用多个迭代器,它可能会在开始时抛出 concurrentModificationException,以防止将来出现意外行为。

如果您使用 seek(0) 将每个新读取器设置为文件的开头,那么从文件中使用多个读取器有什么意义?你的读者似乎试图阅读整个文件

 while (f.getFilePointer()<f.length())
                    System.out.print(f.readInt()+" ");

并且你的作者试图在

中读取单个 int

f.writeInt(n);

因此,如果您启动 n 个线程,我不希望读取 n 个数字。

最后,您是否考虑过在 startRead 解锁 mylock 但 EndRead 再次锁定它之前启动另一个写入器/读取器时会发生什么?那么为什么不把 EndRead 中的逻辑放在 startRead 方法的末尾呢?

【讨论】:

  • 实际上在我的程序中我确实报告了它们,我在这里删除了它,因此代码看起来并不长。并且没有报告异常。
  • 你是对的,它没有多大意义,但如果我删除该行,则不会读取任何数据。
  • 如何从监视器调用您的方法?我的意思是应该启动和运行这一切的代码是什么?你只是循环调用 startWrite、stopWrite、startRead、stopRead 吗?
  • 我在主线程中循环运行 Writer 和 Reader 对象,它们的运行方法只是运行监视器的方法。显然,监视器对象由所有 Writer 和 Reader 对象共享,我将创建的监视器对象的相同引用作为参数传递给它们的构造函数。
  • 您建议将该逻辑放在 startRead 的末尾,但我想使用 Start 和 End 方法来做,然后我可以在 startRead 的开头抓住锁并在末尾释放结束阅读?它会以同样的方式工作吗?另外,我怎样才能让我的读者只阅读一个 int?如果我只是在没有 while 或将指针设置为 0 的情况下执行 f.ReadInt(),则每次阅读器尝试阅读时都会捕获异常。
猜你喜欢
  • 2013-12-27
  • 1970-01-01
  • 1970-01-01
  • 2017-07-04
  • 2022-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
相关资源
最近更新 更多