【问题标题】:Using CountDownLatch & Object.wait inside recursive block hangs在递归块中使用 CountDownLatch 和 Object.wait 挂起
【发布时间】:2015-03-15 20:49:47
【问题描述】:

问题:在尝试分阶段检索递归块内的值时,执行被挂起。

说明: CountDownLatch & Object.wait 用于实现递归块内部值的分阶段访问。但是,程序挂起并显示以下输出:

2 < 16
3 < 16
4 < 16
5 < 16
Current total: 5
 Inside of wait
 Inside of wait

计划:

import java.util.concurrent.*;
public class RecursiveTotalFinder {
    private static CountDownLatch latch1;
    private static CountDownLatch latch2;
    private static CountDownLatch latch3;
    public static void main(String... args) {
       latch1 = new CountDownLatch(1);
       latch2 = new CountDownLatch(1);
       latch3 = new CountDownLatch(1);

       //Create object
       TotalFinder tf = new TotalFinder(latch1,latch2,latch3);

       //Start the thread
       tf.start();

       //Wait for results from TotalFinder
       try {
           latch1.await();
       } catch(InterruptedException ie) {
           ie.printStackTrace();
       }

       //Print the result after 5th iteration
       System.out.println("Current total: "+tf.getCurrentTotal());
       tf.releaseWaitLock();
       tf.resetWaitLock();

       //Wait for results again
       try {
           latch2.await();
       } catch(InterruptedException ie) {
           ie.printStackTrace();
       }

       //Print the result after 10th iteration
       System.out.println("Current total: "+tf.getCurrentTotal());
       tf.releaseWaitLock();
       tf.resetWaitLock();

       //Wait for results again
       try {
           latch3.await();
       } catch(InterruptedException ie) {
           ie.printStackTrace();
       }

       //Print the result after 15th iteration
       System.out.println("Current total: "+tf.getCurrentTotal());
       tf.releaseWaitLock();
       tf.resetWaitLock();
    }
}


class TotalFinder extends Thread{
    CountDownLatch tfLatch1;
    CountDownLatch tfLatch2;
    CountDownLatch tfLatch3;
    private static int count = 1;
    private static final class Lock { }
    private final Object lock = new Lock();
    private boolean gotSignalFromMaster = false;

    public TotalFinder(CountDownLatch latch1, CountDownLatch latch2, 
                       CountDownLatch latch3) {
        tfLatch1 = latch1;
        tfLatch2 = latch2;
        tfLatch3 = latch3;
    }

    public void run() {
        findTotal(16);
    }

    //Find total
    synchronized void findTotal(int cnt) {
        if(count%5==0) {
           if(count==5)
              tfLatch1.countDown();
           if(count==10)
              tfLatch2.countDown();
           if(count==15)
              tfLatch3.countDown();

           //Sleep for sometime
           try {
               Thread.sleep(3000);
           } catch(InterruptedException ie) {
               ie.printStackTrace();
           }
           //Wait till current total is printed

           synchronized(lock) {
              while(gotSignalFromMaster==false) {
                 try {
                    System.out.println(" Inside of wait");
                    lock.wait();
                 } catch(InterruptedException ie) {
                    ie.printStackTrace();
                 }
              }
              System.out.println("Came outside of wait");
           }

        }
        count +=1;
        if(count < cnt) {
           System.out.println(count +" < "+cnt);
           findTotal(cnt);
        }
    }

    //Return the count value
    public int getCurrentTotal() {
       return count;
    }

    //Release lock
    public void releaseWaitLock() {
        //Sleep for sometime
        try {
            Thread.sleep(5000);
        } catch(InterruptedException ie) {
            ie.printStackTrace();
        }

        synchronized(lock) {
           gotSignalFromMaster=true;
           lock.notifyAll();
        }
    }

    //Reset wait lock
    public void resetWaitLock() {
        gotSignalFromMaster = false;
    }
}

分析: 在我的初步分析中,尽管从主程序调用了 notifyAll,但它似乎正在递归地发生等待。

帮助: 为什么在CountDownLatch 之后使用notfiyAll 的free lock 没有生效?需要有人帮助了解此程序中到底发生了什么。

【问题讨论】:

  • 如果让 gotSignalFromMaster 易失怎么办?
  • 我发现“低级”并发(如等待)与“高级”并发实用程序(如 countdownlatch)的混合很难遵循。基本上,您似乎是从主线程发出信号,表明另一个可以继续 - SynchronizedQueue 之类的东西(主线程在打印后将某些内容推送到队列中,其他线程正在等待接受它)来实现相同的目标?
  • 我当然是指“SynchronousQueue”。

标签: java recursion concurrency wait countdownlatch


【解决方案1】:

我从 JCIP 得到的关于 waitnotify 的主要信息是我可能会错误地使用它们,所以除非绝对必要,否则最好避免直接使用它们。因此,我认为您应该重新考虑使用这些方法。

在这种情况下,我认为您可以使用SynchronousQueue 更优雅地做到这一点。也许这样的事情可能会起作用:

import java.util.concurrent.*;
public class RecursiveTotalFinder {
    public static void main(String... args) throws InterruptedException {
       SynchronousQueue<Integer> syncQueue = new SynchronousQueue<>();

       //Create object
       TotalFinder tf = new TotalFinder(syncQueue, 5);

       //Start the thread
       tf.start();

       for (int i = 0; i < 3; ++i) {
         System.out.println("Current total: " + syncQueue.take());
       }
    }
}

class TotalFinder extends Thread{
  private final SynchronousQueue<Integer> syncQueue;
  private final int syncEvery;
  private int count;

  public TotalFinder(SynchronousQueue<Integer> syncQueue, 
                     int syncEvery) {
    this.syncQueue = syncQueue;
    this.syncEvery = syncEvery;
  }

  public void run() {
    try {
      findTotal(16);
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      throw new RuntimeException(e);
    }
  }

  //Find total
  void findTotal(int cnt) throws InterruptedException {
    if((count > 0) && (count%syncEvery==0)) {
      syncQueue.put(count);
    }
    count +=1;
    if(count < cnt) {
      System.out.println(count +" < "+cnt);
      findTotal(cnt);
    }
  }
}

至于为什么你的原始方法不起作用,这是因为主线程将gotSignalFromMaster设置为true,然后立即返回false,这发生在另一个线程能够检查其值之前。如果您在resetWaitLock 中稍作休息,它会继续超出当前挂起的位置;但是,它会在最后挂起而不是终止。

请注意,必须使用Thread.sleep 来等待另一个线程更改某些状态是一种糟糕的方法——尤其是因为它会使您的程序非常慢。使用同步实用程序可以更快、更容易推理程序。

【讨论】:

  • 非常感谢您为这种不同的方法付出的时间和精力,它按预期工作。不过,知道为什么在我的程序中观察到挂起会很有趣。我将使用你的逻辑来继续我的功能。
  • 非常感谢,现在我解决了这个问题。感谢您的帮助。
猜你喜欢
  • 2018-06-16
  • 2015-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-10
  • 2021-09-06
相关资源
最近更新 更多