简体   繁体   中英

Ensure that java thread is really suspended

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

Now I have a list of its objects:

private final List<PawnThread> pawnThreadList;

I defined some helper method to suspend all of them:

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

Now suspend() method is only about changing flag. The requirement is, that when I leave suspendAll() method, all threads should be actually paused (they cannot be in RUNNABLE state) - for now it is not a case, beacause for some of them, it may take some time to actually finish their job before pause.

I would be grateful for advice what is correct design for this soulution.

Regards

The requirement is impossible to satisfy, but also makes no sense. In order for the thread to communicate the fact that it has suspended, the thread must be running. There is no way to ensure the thread has completed the suspension process.

But this is also not a sensible requirement. How can it possibly matter whether the thread has suspended itself or is about to suspend itself, so long as it has nothing left to do but suspend itself?

A sensible requirement should be satisfied by having each thread set some indication somewhere that it has received the suspend request and is about to stop executing. Then the calling thread can wait for all threads to have provided that indication.

Universal correct design for any parallel solution is to define streams of tokens and firing rule (see Petry Net tedminology). Most simple and useful firing rule is to start an action when all input tokens are ready. I your case, input tokens are hidden in whle condition and in suspend condition. Your mistake is you defined suspend condition as negative, while all tokens must be defined as positive. That is, a thread works where there are enough tokens, and stops when they are exhausted, and then thread waits while the number of tokens is increased by external threads.

Tokens may be of 2 kinds - black (pure permissions), passed by Semaphores, and color (messages), passed by BlockingQueues. These 2 communicator classes cover most of use cases. In some complex cases, user can create custom communicators using synchronized/wait/notify.

So canonical way to design any parallel program is as follows:

  • design Petry Net, with places for tokens (communicators), and transitions (actions).

  • map places to Semaphores/BlockingQueues/CustomCommunicators, and transition to threads (or Actors).

Make PawnThread#suspend() wait for suspension to be completed:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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