【问题标题】:Ensure that java thread is really suspended确保java线程真的被挂起
【发布时间】:2021-03-11 09:43:32
【问题描述】:

我有以下课程:

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 状态) - 现在不是这样,因为对于其中一些,可能需要一些时间在暂停之前真正完成他们的工作。

我将不胜感激此解决方案的正确设计。

问候

【问题讨论】:

    标签: java multithreading wait notify suspend


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

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

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

      所以设计任何并行程序的规范方法如下:

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

      • 将位置映射到 Semaphores/BlockingQueues/CustomCommunicators,并转换到线程(或 Actor)。

      【讨论】:

        【解决方案3】:

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

        但这也不是一个明智的要求。线程是否已经挂起或将要挂起自己又有什么关系呢,只要它除了挂起自己之外别无他法吗?

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

        【讨论】:

        • 感谢您的解释!我试图通过将CountDownLatch 传递给所有线程来执行您在第三段中所说的操作,但这不是很好的方法 - 它只能使用一次(它不是循环的),而且一些线程可能会终止,所以固定大小闩锁不是很好。您知道哪种机制最适合吗?
        • @MikolajMGT 为什么不只计算正在运行的线程数?
        猜你喜欢
        • 1970-01-01
        • 2013-11-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-27
        • 2014-10-23
        • 1970-01-01
        相关资源
        最近更新 更多