使用CountDownLatch& Object.wait里面的递归块挂起

时间:2015-03-15 20:49:47

标签: java recursion concurrency wait countdownlatch

问题:尝试以分阶段方式检索递归块内的值时,执行会挂起。

说明: 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;
    }
}

分析: 在我最初的分析中,看起来等待是在主程序调用{​​{1}}时递归发生的。

帮助 为什么在notifyAll生效后使用notfiyAll免费锁定?需要有人帮助了解此计划中究竟发生了什么。

1 个答案:

答案 0 :(得分: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等待另一个线程更改某个状态是一种糟糕的方法 - 尤其是因为它会使您的程序变得非常慢。使用同步实用程序可以更快,更容易理解程序。