如何在Java中使用锁来等待特定条件?

3

我有一个对象:

public class Resource {
    private Lock lock = new ReentrantLock();
    private boolean processed = false;

    public Lock getLock() {
        return lock;
    }

    public boolean isProcessed() {
        return processed;
    }

    public void setProcessed(boolean processed) {
        this.processed = processed;
    }
}

我希望能够停止线程“one”,直到线程“two”将变量“processed”更改为true。在“processed”设置为true后,我希望唤醒线程“one”并继续进行一些操作。
我知道我们可以使用wait和notify方法来组织它,但这非常危险,因为可能会发生中断。
如果我只使用wait和notify方法,可能会出现无限等待的情况。
如果我们的等待方法由于某种原因被中断,在检查“process”变量仍为false之后,我们可以再次使用wait,如下所示:
while(true){
    if(!resource.isProcessed()){
       resource.getLock().wait();
    }
    else{
       break;
    }
}

使用这样的代码是危险的,因为在我们检查了"!resource.isProcessed()"并且在使用"resource.getLock().wait()"之前,另一个进程可以将"process"设置为true并调用"resource.getLock().notify()"(这不会产生任何效果,因为我们还没有调用"wait()")。

如何安全地等待某些条件?如何安全地通知/解锁某些条件?


2
我建议您等待一个条件(Condition) https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Condition.html 请参考Javadoc中的例子。 - Peter Lawrey
2个回答

1

正如Peter Lawrey在评论中所回答的那样,Java中有Condition可用。(感谢您的指出)

这是一个可在文档中找到的示例:

 class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 

   final Object[] items = new Object[100];
   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length)
         notFull.await();
       items[putptr] = x;
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } finally {
       lock.unlock();
     }
   }

   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0)
         notEmpty.await();
       Object x = items[takeptr];
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } finally {
       lock.unlock();
     }
   }
 }

0

您可以使用CountDownLatch使一个线程等待,直到另一个线程执行的操作完成。

假设T1和T2是您的线程,并且它们共享一个初始化为计数器1CountDownLatch。 T1将首先在门闩上调用await(),而T2应执行其操作,然后在门闩上调用countDown()以让T1继续。

当然,T1中的await()仍然可能被中断,因此您可能希望在循环中调用它。

class T1 implements Runnable {
   private final CountDownLatch latch;

   T1(CountDownLatch latch) {
     this.latch = latch;
   }

   public void run() {
      awaitUninterruptibly(latch);
      doWork();
   }

   private void awaitUninterruptibly(CountDownLatch latch) {
      boolean interrupted = false;
      try {
         while (true) {
            try {
               latch.await();
               return;
            } catch (InterruptedException e) {
               interrupted = true;
            }
         }
      } finally {
         if (interrupted) {
            Thread.currentThread().interrupt();
         }
      }
   }
}

class T2 implements Runnable {
   private final CountDownLatch latch;

   T1(CountDownLatch latch) {
      this.latch = latch;
   }

   public void run() {
      doWork();
      latch.countDown();
   }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接