2014-12-26 58 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