繁体   English   中英

唤醒线程而不冒被阻塞的风险

[英]Waking up a thread without risking to get blocked

我有一个无限期运行的工作线程,如果无事可做,它将进入睡眠状态一分钟。 有时,另一段代码会产生一些工作,并且想要立即唤醒工作线程。

所以我做了这样的事情(仅用于说明的代码):

class Worker {
    public void run() {
        while (!shuttingDown()) {
           step();
        }
    }

    private synchronized void step() {
        if (hasWork()) {
            doIt();
        } else {
            wait(60_000);
        }
    }

    public synchronized wakeMeUpInside() {
        notify();
    }
}

我不喜欢的只是进入监视器才能唤醒某些东西,这意味着通知线程可能没有充分的理由被延迟。 由于本机同步的选择是有限的,我以为我会切换到Condition ,但是它有完全相同的问题

当调用此方法时,实现可能(并且通常确实)要求当前线程持有与此Condition相关联的锁。

这是一个基于信号量的解决方案:

class Worker {
    // If 0 there's no work available
    private workAvailableSem = new Semaphore(0);

    public void run() {
        while (!shuttingDown()) {
           step();
        }
    }

    private synchronized void step() {
        // Try to obtain a permit waiting up to 60 seconds to get one
        boolean hasWork = workAvailableSem.tryAquire(1, TimeUnit.MINUTES);
        if (hasWork) {
            doIt();
        }
    }

    public wakeMeUpInside() {
        workAvailableSem.release(1);
    }
}

我不确定100%符合您的需求。 注意事项:

  • 每次调用wakeMeUpInside都会添加一个许可。 因此,如果两个线程唤醒了Worker ,它将在没有阻塞的情况下运行doIt两次。 您可以扩展示例以避免这种情况。
  • 这需要等待60秒才能完成工作。 如果没有可用的方法,它将返回到run方法,该方法将立即将其发送回step方法,后者将再次等待。 我这样做是因为我假设您有某种原因,即使您没有工作,您也希望每60秒运行一次。 如果不是这种情况,请致电aquire ,您将无限期地等待工作。

根据下面的注释,OP只能运行一次。 在这种情况下,虽然可以调用drainPermits ,但更干净的解决方案是仅使用LockSupport如下所示:

class Worker {
    // We need a reference to the thread to wake it
    private Thread workerThread = null;
    // Is there work available
    AtomicBoolean workAvailable = new AtomicBoolean(false);

    public void run() {
        workerThread = Thread.currentThread();
        while (!shuttingDown()) {
           step();
        }
    }

    private synchronized void step() {
        // Wait until work is available or 60 seconds have passed
        ThreadSupport.parkNanos(TimeUnit.MINUTES.toNanos(1));
        if (workAvailable.getAndSet(false)) {
            doIt();
        }
    }

    public wakeMeUpInside() {
        // NOTE: potential race here depending on desired semantics.
        // For example, if doIt() will do all work we don't want to
        // set workAvailable to true if the doIt loop is running.
        // There are ways to work around this but the desired
        // semantics need to be specified. 
        workAvailable.set(true);
        ThreadSupport.unpark(workerThread);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM