确保 java 线程确实被挂起

Ensure that java thread is really suspended

我关注class:

public class PawnThread implements Runnable {

    public void start() {
        thread.start();
    }

    @Override
    public void run() {
        try {
            while (... some finish condition ...) {
                move();
                synchronized (this) {
                    while (suspendFlag) {
                        wait();
                    }
                }
            }
        } catch (InterruptedException e) {
            System.err.println(pawn.toString() + ": thread interrupted :(");
        }
    }

    void move() {
        ... some blocking actions
    }

    synchronized void suspend() {
        suspendFlag = true;
    }

    synchronized void resume() {
        suspendFlag = false;
        notify();
    }
}

现在我有了它的对象列表:

private final List<PawnThread> pawnThreadList;

我定义了一些辅助方法来暂停所有这些:

public void suspendAll() {
   pawnThreadList.forEach(PawnThread::suspend);
}

现在suspend()方法只是改变标志。要求是,当我离开 suspendAll() 方法时,所有线程实际上都应该暂停(它们不能处于 RUNNABLE 状态) - 现在不是这种情况,因为对于其中一些,它可能在暂停之前花一些时间真正完成他们的工作。

对于此解决方案的正确设计是什么,我将不胜感激。

此致

要求无法满足,也没有意义。为了让线程传达它已挂起的事实,线程必须 运行。无法确保线程已完成挂起过程。

但这也不是一个明智的要求。只要线程已经挂起自己或即将挂起自己,它有什么关系呢?

通过让每个线程在某处设置一些指示它已收到挂起请求并即将停止执行来满足合理的要求。然后调用线程可以等待所有线程提供该指示。

任何并行解决方案的通用正确设计是定义令牌流和触发规则(参见 Petry Net tedminology)。最简单有用的触发规则是在所有输入令牌准备就绪时开始操作。在您的情况下,输入令牌隐藏在全条件和暂停条件下。您的错误是您将暂停条件定义为负数,而所有标记都必须定义为正数。即一个线程在token够多的地方工作,用完就停止,然后线程等待,等待外部线程增加token个数。

令牌可能有 2 种类型 - 黑色(纯权限),由信号量传递,以及颜色(消息),由 BlockingQueues 传递。这 2 个通信器 类 涵盖了大多数用例。在一些复杂的情况下,用户可以使用 synchronized/wait/notify.

创建自定义通信器

因此,设计任何并行程序的规范方法如下:

  • 设计 Petry Net,其中包含标记(通信器)和转换(动作)的位置。

  • 将位置映射到 Semaphores/BlockingQueues/CustomCommunicators,并过渡到线程(或 Actors)。

使PawnThread#suspend()等待暂停完成:

public class PawnThread implements Runnable {
    private final Waiter suspender = new Waiter();
    private final Waiter suspending = new Waiter();

    @Override
    public void run() {
        try {
            while (...) {
                suspending.suspend();
                move();
                suspending.resume();
                suspender.await();
            }
        } catch (InterruptedException e) {
            ...
        }
    }

    void suspend() throws InterruptedException {
        suspender.suspend();
        suspending.await();
    }

    void resume() {
        suspender.resume();
    }
}

public class Waiter {
    private boolean waiting;

    public synchronized void await() throws InterruptedException {
        while (waiting) {
            wait();
        }
    }

    public synchronized void suspend() {
        waiting = true;
    }

    public synchronized void resume() {
        waiting = false;
        notify();
    }
}