在循环期间线程等待

时间:2014-12-26 12:55:51

标签: java multithreading concurrency

我的应用程序有1个全局驱动程序,负责执行低级别的工作。 然后我有2个线程,它们都使用无限循环来完成一些工作。我的问题是如何允许1个线程尽可能多地使用驱动程序,但是有必要让第二个线程在必要时使用它。

详细说明,我的代码如下:

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(),第二个线程只获取锁以完成小任务,然后将锁返回给第一个线程。

使用此代码,第一个线程将继续使用控制器执行它需要执行的操作,而第二个线程在其synchronized块中等待等待。我目前修复此问题的方法是在第一个线程中添加一个暂停,为第二个线程提供获取锁定的机会,如下所示:

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();
}

这确实按预期工作,但似乎不对。我不满意每次迭代后的手动暂停,特别是如果第二个线程不需要锁定,因为它浪费时间。

我如何更换暂停以提高效率?

5 个答案:

答案 0 :(得分:1)

为什么在synchronized中使用run()?在synchronized

中的方法中使用LockWebDriverController
public void doSomeThingA(){
   lock.lock();
   try {
      //your stuff
   } finally {
       lock.unlock();
   }
}

在Thread的run方法中调用这些方法。

答案 1 :(得分:0)

我认为你正在从错误的方向接近这一点,因为在你当前的设置中,99.999%的时间线程A要求监视器处理时间被浪费。但是,由于我没有足够的有关您实际问题的详细信息,因此这是一个使用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立即(在任何线程切换发生之前)再次调用它。

答案 2 :(得分:-1)

这里有一些选择。在这个实例中最好的选择可能是消除决定何时从线程执行工作的责任,而是等待监视器中的事件释放线程以执行工作。然后,您可以按照最适合目的的比例安排工作。

或者,从控制器代码中删除缺少线程安全性。

答案 3 :(得分:-1)

假设上面的线程组织是你的特定情况的最佳方式,你的问题是第一个线程持有锁太长,从而使第二个线程挨饿。

你可以检查doSomethingA函数在执行时是否真的需要锁定驱动程序(在大多数情况下它不会),如果没有将它拆分成多个较小的执行块,其中一些执行锁定而其他人则没有。这将为第二个线程创建更多时间,以便在需要时启动。

如果无法做到,那么您确实需要重新考虑您的应用,因为您已经创建了资源瓶颈。

答案 4 :(得分:-2)

看起来Thread.yield ()正是您要找的。