2016-06-11 44 views
3
Lock sharedLock = new ReentrantLock(); 
Condition condition = lock.newCondition(); 

主線:多線程:Java的狀態等待超時,但不能返回

sharedLock.lock(); 
childThread.start(); 
condition.await(5, TimeUnit.SECONDS); 
sharedLock.unlock(); 

子線程:

sharedLock.lock(); 
//do something, may take a long time 
Thread.sleep(10);// sleep to simulate a long execution 
condition.signal(); 
sharedLock.unlock(); 

假設子線程發送一個網絡請求並等待響應,我要主線程最多等待5秒,如果超時,請重試請求。但是當await()超時時,它不能獲取鎖,因爲子線程仍然持有它,所以它仍然等待鎖直到子線程釋放它,這需要10秒。

我怎樣才能達到我的要求主線程等待子線程的信號,但有一個有界的超時?

+0

您是否需要使用'Lock'和'Condition'(如在作業中)?有比簡單的解決方案,而不是建立這樣一個大多數的鎖定和條件的嘗試機制。 – zapl

+0

你的sharedLock的目的是什麼?你想用它來防止什麼? –

+0

@NicolasFilotto我想使用信號並等待。 condition.await和condition.signal必須與鎖一起使用,或者不能調用await和signal方法。 – Moon

回答

1

這不是你應該如何做到這一點,你應該:

  1. 創建ExecutorService(線程池)爲您應檢查類Executors的方法來選擇最好的一個你的情況,但Executors.newFixedThreadPool是一個良好的開端
  2. 提交你的任務作爲FutureTask線程池
  3. 然後調用get一個超時
  4. 管理得當TimeoutException

這裏是它如何做到:

// Total tries 
int tries = 3; 
// Current total of tries 
int tryCount = 1; 
do { 
    // My fake task to execute asynchronously 
    FutureTask<Void> task = new FutureTask<>(
     () -> { 
      Thread.sleep(2000); 
      return null; 
     } 
    ); 
    // Submit the task to the thread pool 
    executor.submit(task); 
    try { 
     // Wait for a result during at most 1 second 
     task.get(1, TimeUnit.SECONDS); 
     // I could get the result so I break the loop 
     break; 
    } catch (TimeoutException e) { 
     // The timeout has been reached 
     if (tryCount++ == tries) { 
      // Already tried the max allowed so we throw an exception 
      throw new RuntimeException(
       String.format("Could execute the task after %d tries", tries), 
       e 
      ); 
     } 
    } 
} while (true); 

我如何能實現我的要求,即主線程等待子線程的 信號,但有一個有限的超時?

這裏是你如何能達到你的要求:

主線:

lock.lock(); 
try { 
    childThread.start(); 
    condition.await(5, TimeUnit.SECONDS); 
} finally { 
    sharedLock.lock(); 
} 

的子線程:

try { 
    //do something, may take a long time 
    Thread.sleep(10);// sleep to simulate a long execution 
} finally { 
    // Here we notify the main thread that the task is complete whatever 
    // the task failed or not 
    lock.lock(); 
    try { 
     condition.signal(); 
    } finally { 
     lock.unlock(); 
    } 
} 

正如你所看到的工作,任務必須不在關鍵部分內執行,我們只獲取鎖定以通知主線程。否則,如果在超時之後在臨界區內執行任務,主線程仍需要再次獲取鎖,並且由於鎖實際上由子線程擁有,所以它將需要等待直到任務結束使超時完全無用。

注:我改名sharedLocklockReentrantLock是一個排他鎖不是共享鎖,如果你需要一個共享鎖檢查類Semaphore定義許可證的總量。

+0

非常感謝,這很有道理。但是我仍然想知道在什麼情況下,condition.await(timeout,TimeUnit)會有意義。 await方法在返回之前需要鎖。在正常情況下,它從另一個釋放鎖的線程收到一個信號,所以等待可以返回。但在超時的情況下,另一個線程仍然保持鎖定狀態。所以await()方法中的超時參數不會生效。 – Moon

+0

非常感謝! – Moon

+0

我看到我的回答曾經是被接受的答案。我很高興這個答案比我的更有幫助。 Buf如果我的回答對你有點幫助,你會介意upvote和這個嗎?我在回答中做了很多工作。謝謝。 – waltersu

1

您的代碼可以用intrinsic lock簡化。

Object sharedObj = new Object(); 

主線:

synchronized (sharedObj) { 
     int retryCount = 0; 
     while (retryCount < maxRetry) { 
     sharedObj.wait(5000); 
     retryCount++; 
     } 
    } 

子線程:

synchronized (sharedObj) { 
     //do something, may take a long time 
     Thread.sleep(10);// sleep to simulate a long execution 
     sharedObj.notify(); 
    } 

java的狀態等待超時,但不能返回

這是因爲鎖必須被釋放所以等待/等待才能返回。所以,你的子線程應該是這樣的:

//do something, may take a long time 
    Thread.sleep(10);// sleep to simulate a long execution 
    synchronized (sharedObj) { 
     sharedObj.notify(); 
    } 

Java的等待/通知通常是用來解決生產者 - 消費者問題。 而且通常sharedObj不應該持有太久。然後你的主線程可以在等待超時時再次保持鎖定。

看看一個在生產例如:hadoop/hdfs/DFSOutputStream.java 的邏輯很簡單,生產者創建數據包,並把它放在dataQueue

// takes a long time to create packet 
synchronized (dataQueue) { 
    dataQueue.addLast(packet); 
    dataQueue.notifyAll(); 
} 

消費者等待而dataQueue是空的:

synchronized (dataQueue) { 
     while ((!shouldStop() && dataQueue.size() == 0 &&...) { 
     try { 
      dataQueue.wait(timeout); 
     } catch (InterruptedException e) { 
      LOG.warn("Caught exception", e); 
     } 
     doSleep = false; 
     now = Time.monotonicNow(); 
     } 

正如你所看到的,dataQueue大多數時間都是解鎖的!

我怎樣才能達到我的要求,主線程等待子線程的信號,但有一個有界的超時?

如果你的子線程大多是在一個循環,你的主線程可以設置isRunning標誌本身,使子線程停止。如果你的子線程主要被I/O操作阻塞,你的主線程可能會中斷子線程。

sharedObj用於協調和保護sharedObj。如果有其它資源應該得到保護,你有2種選擇:
1.如果資源上的操作快捷,像ackQueueDFSOutputStream.java,一起保護它sharedObj內。
2.如果資源上的操作比較耗時,請在sharedObj以外的地方進行操作。