2014-12-26 83 views
1

我的應用程序有1個全局驅動程序,負責做低級別的工作。 然後我有2個線程,這兩個線程使用無限循環來完成一些工作。我的問題是如何讓1個線程儘可能地使用驅動程序,但給第二個線程在必要時使用它的機會。while while循環等待期間

爲了詳細說明,我的代碼如下:

public class Game { 
    private static final Object LOCK = new Object(); 
    private static final Logger LOGGER = Logger.getLogger(Game.class); 

    private WebDriverController controller; 

    public Game(WebDriverController controler) { 
     this.controller = controller; 
    } 

    public void startThreadA() { 
     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       while (true) { 
        synchronized (LOCK) { 
         controller.doSomethingA(); 
        } 
       } 
      } 
     }).start(); 
    } 

    public void startThreadB() { 
     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       while (true) { 
        ... 
        ... 
        synchronized (LOCK) { 
         controller.doSomethingB(); 
        } 
        ... 
        ... 
       } 
      } 
     }).start(); 
    } 
} 

的邏輯是允許第一線程執行doSomethingA()儘可能,與第二線程只有獲取鎖來完成的小任務然後將鎖返回到第一個線程。

使用此代碼,第一個線程將持續使用控制器來完成它需要做的事情,而第二個線程在其同步塊中等待。目前我已經解決了這個問題的方法是通過將暫停到第一線,給第二個線程有機會獲得鎖,如下所示:

public void startThreadA() { 
    new Thread(new Runnable() { 
     @Override 
     public void run() { 
      while (true) { 
       synchronized (LOCK) { 
        controller.doSomethingA(); 
       } 
       try { 
        Thread.sleep(1); 
       } catch (InterruptedException e) { 
        LOGGER.error(null, e); 
       } 
      } 
     } 
    }).start(); 
} 

這正好沒有工作的打算,但它不」 t看起來沒錯。我對每次迭代後的手動暫停都不滿意,尤其是如果第二個線程不需要鎖定,因爲它浪費了時間。

我該如何更換暫停以提高效率?

+0

如果第二個線程只執行小任務,那麼也可以將工作移動到第一個線程中。這將使您的代碼更簡單,可能更有效。 –

+0

@PeterLawrey任務很小,但有很多。這個想法是,只要第二個線程不需要驅動程序來執行其任務,就允許第一個線程執行。 – Cristian

+0

由於只有一個線程可以持有鎖,因此您實際上只能有一個正在運行的線程。除非線程可以併發/獨立執行,否則只會增加複雜性。 –

回答

0

我覺得你正在從錯誤的方向來處理這個問題,就像在你當前的設置中一樣,線程A調用監視器的時間爲99.999%,處理時間被浪費了。然而,由於我沒有你的實際問題的足夠的細節,這裏是用的ReentrantLock與公平調度(FIFO)一個快速的解決方案:

protected final ReentrantLock lock = new ReentrantLock(true); // fair scheduling 

public void functionA() { 
    lock.lock(); 
    try { 
    controller.functionA(); 
    } finally { 
    lock.unlock(); 
    } 
} 

public void functionB() { 
    lock.lock(); 
    try { 
    controller.functionB(); 
    } finally { 
    lock.unlock(); 
    } 
} 

說明:

如果線程A目前持有鎖定和線程B調用,B保證在A釋放後立即接收顯示器,即使A立即(在任何線程切換髮生之前)再次調用它。

+0

「線程B保證接收顯示器正確的連接...」顯然是錯誤的。該代碼在功能上等同於OP的原始版本,沒有睡眠,並且可能會以同樣的方式捱餓資源的第二個線程。 – Dima

+0

不,不是。公平調度保證呼叫按照順序進行處理,而不是像隨機同步一樣進行半隨機處理。 – TwoThe

+0

問題不在於獲取鎖定的順序,而在於確保發生上下文切換。你需要提示調度器,你的線程不介意等一下。 – Dima

-1

這裏有幾個選項。在這種情況下,最好的辦法是去掉決定何時從線程開始工作的責任,而是等待監視器發出的事件釋放線程來完成工作。然後,您可以按照最適合該目的的比例安排工作。

或者,從控制器代碼中刪除線程安全性的缺失。

-1

假設上面的線程組織是針對您的特定情況的最佳方式,那麼您的問題是第一個線程將鎖持續時間過長,從而導致第二個線程捱餓。

您可以檢查doSomethingA函數在執行時是否真的需要鎖定驅動程序(大多數情況下不會),並且如果不將它分割成多個較小的執行塊,其中一些塊會保持鎖定狀態別人不會。這將爲第二個線程創建更多時間,以便在需要時啓動。

如果無法完成,那麼您確實需要重新考慮您的應用程序,因爲您已經創建了資源瓶頸。

-2

它看起來像Thread.yield()是你在找什麼。

+0

讓我引用文檔爲什麼這是一個壞主意:「調度程序可以自由地忽略這個提示。」和「很少適合使用這種方法,它可能對調試或測試有用」 – TwoThe

+0

不知道你如何解釋這個摘要是「這是一個壞主意」......也許,你有一個更好的處理英語語言比我做的更好...... – Dima

+0

根據文檔「可能會或可能不會工作」編寫一個「解決方案」會產生可能或可能無法工作的代碼。如果它在你的機器上工作,它不能保證在目標機器上工作,所以你可能在發佈期間坐在那裏花費整晚尋找問題。因此這是一個壞主意。 – TwoThe

1

爲什麼在run()中使用​​?在WebDriverController的方法中使用​​或Lock

public void doSomeThingA(){ 
    lock.lock(); 
    try { 
     //your stuff 
    } finally { 
     lock.unlock(); 
    } 
} 

而在Thread的run方法中調用這些方法。

+0

感謝您的回答。 WebDriverController鎖是一個類變量,由類的方法使用嗎? – Cristian

+0

是的。在您的WebDriverController中,您可以使用Lock實現 –

+0

之一好的謝謝,我將把鎖定功能移出運行方法並進入控制器。 – Cristian