【问题标题】:Synchronisation problem on a variation of the classic bounded buffer problem using java使用 java 的经典有界缓冲区问题的变体上的同步问题
【发布时间】:2011-04-05 12:34:37
【问题描述】:

我对经典的有界缓冲区问题做了一个变体。

这是一个很大的不同,我需要了解线程安全的一个小技术障碍(而不是无休止地测试可能永远不会发生的情况)

我认为我不需要解释以下代码示例的评估情况

if(buffer1.BufferNotFull)
{
    buffer1.Lock();
    buffer1.AddValueAtIndex(value, index);
    buffer1.Unlock();
}

基本上,我的 buffer1 有一个简单的条件变量(已更新和全部),它让我知道它是否未满,然后我获取一个锁,施展魔法,然后释放锁。

但是我可以简单地假设评估条件“是 buffernotfull”不能与另一个线程交错,只是在我的缓冲区中的最后一个插槽上写入?

(缓冲区其实是一个值数组,-1代表空,别问为什么^_^)

简而言之:就是评价

if(buffer1.BufferNotFull)

主体代码获取锁的线程安全

编辑

这是我现在用于整个系统的系统。我希望它有助于展示我的设计。考虑到我做这个设计纯粹是为了学习这些基本的同步机制。

访问缓冲区的线程的控制流

读取输入流(无需同步)

尝试:

  1. 来自生产者线程的写操作 在缓冲区 1 上请求锁定:
  2. 如果锁定被拒绝,继续请求 锁。
  3. 如果不满足条件变量, 等等
  4. 如果接受锁,执行写入 操作
  5. 解锁缓冲区 1 并通知其他 等待线程。
  6. 如果输入流为空且缓冲区 1 是死并请求死亡 过滤线程

尝试:

  1. 从过滤器读取操作 buffer1 上的线程请求锁定:
  2. 如果锁被拒绝,继续请求锁
  3. 如果接受锁,则执行读取 操作(将值替换为 -1) 并将值存储在线程中 应用过滤器后。
  4. 如果不满足条件变量, 等等。
  5. 解锁缓冲区 1 并通知其他 等待线程。
  6. 如果缓冲区 1 和缓冲区 2 为空并且 存在死亡请求,死亡。

尝试:

  1. 来自过滤器的写操作 buffer2 上的线程请求锁定:
  2. 如果锁被拒绝,继续请求锁
  3. 如果接受锁,执行写入 操作(与过滤 结果)。
  4. 如果不满足条件变量, 等等。
  5. 解锁缓冲区2并通知其他 等待线程。
  6. 如果缓冲区 1 和缓冲区 2 为空并且 存在死亡请求,发送死亡 请求消费者线程并死掉。

尝试:

  1. 对缓冲区 2 的读取操作从 消费者线程
  2. 如果锁被拒绝,继续请求锁
  3. 如果接受锁,则执行读取 操作(将值替换为 -1) 并直接输出变量 到控制台上。
  4. 如果不满足条件变量, 等等。
  5. 解锁缓冲区 2 并通知其他 等待线程。
  6. 如果缓冲区 2 为空且死亡 请求存在,死亡。

各个线程操作的控制流

获取锁 检查条件变量 如果是,写/读操作 如果NO,等待notfiy 解锁

我将构建和测试实现的最后一部分并进行编辑,以查看接受的答案是否确实给我带来了结果,但这可能需要一些时间

【问题讨论】:

    标签: java multithreading thread-safety theory


    【解决方案1】:

    通常,在上述场景中,您必须先获取锁,然后测试条件是否为真:

    buffer1.Lock();
    
    try {
    
        while( !buffer1.BufferNotFull ) {
    
              buffer1.WaitOnConditionVariable();
        }
    
        buffer1.AddElement(...);
    
    } finally {
    
        buffer1.Unlock();
    }
    

    操作的任何其他顺序都会带来“检查时间/使用时间”竞争条件的风险:考虑以下场景,当前线程在使用BufferNotFull 检查状态后立即被抢占,但是在抓住锁之前。如果新线程向缓冲区添加新元素,则缓冲区的状态可能再次变为“满”。当原始线程恢复时,它会因此在尝试添加自己的新元素时触发“缓冲区已满”异常(或其他),即使它正确地进行了检查(从它自己的角度来看)。

    编辑条件变量将在“等待”操作期间实际进入休眠状态之前释放锁。这是原子完成的,即线程将在锁被释放的“同一时刻”休眠。当变量被其他线程通知时,它会在调用“WaitOnConditionVariable”后重新获取锁,然后再继续执行。

    从缓冲区中删除元素的代码可能如下所示:

    buffer1.Lock();
    
    try {
    
        if( !buffer.IsEmpty ) { 
    
             ... remove element ...
             BufferNotFull = true;
             NotifyConditionVariable();
        }
    } finally {
        buffer1.Unlock();
    }
    

    或者,让它看起来像 Java:

    Object monitor = new Object();
    boolean full = false;
    boolean empty = true;
    

    添加元素:

    synchronized( monitor ) {
        while( full ) {
            monitor.wait();
        }
    
        ... add element ...
        ... maybe set full ...
        empty = false;
        monitor.notifyAll();
    }
    

    移除元素:

    synchronized( monitor ) {
        while( empty ) monitor.wait();
    
        ... remove element ...
        ... maybe set empty ...
        full = false;
        monitor.notifyAll();
    }
    

    【讨论】:

    • 谢谢!我相信这应该可以解决问题!我解决问题的顺序就是问题!让我测试一下
    • 如果您在锁定状态期间不断检查条件更改。你怎么能期待改变?如果在评估 (IsBufferFull) 条件期间缓冲区被填充并且它被认为是真的,那么 while 循环只会继续检查一个不可变的变量,对吗?我需要为此部分使用等待/通知吗?
    • 我知道从线程的角度评估缓冲区状态没有考虑状态多线程概念,其中事物在其自身范围的流程之外发生变化。谢谢你,让我在comment2中发布关于while循环的问题。
    • 应该这样做,绝对给我足够的信息来处理并回答我的第二条评论的问题,它只是让线程等待通知。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 2016-07-31
    • 2018-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多