确保java线程确实已挂起
我有以下课程:确保java线程确实已挂起,java,multithreading,wait,notify,suspend,Java,Multithreading,Wait,Notify,Suspend,我有以下课程: public class PawnThread implements Runnable { public void start() { thread.start(); } @Override public void run() { try { while (... some finish condition ...) { move();
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 pawnThreadList代码>
我定义了一些助手方法来挂起所有这些方法:
public void suspendAll() {
pawnThreadList.forEach(PawnThread::suspend);
}
现在suspend()
方法只是关于更改标志。要求是,当我离开suspendAll()
方法时,所有线程都应该实际暂停(它们不能处于RUNNABLE
状态)-目前情况并非如此,因为对其中一些线程来说,在暂停之前实际完成任务可能需要一些时间
我将不胜感激的建议,什么是正确的设计为这个解决方案
关于要求不可能满足,但也没有意义。为了让线程传递它已挂起的事实,线程必须正在运行。无法确保线程已完成暂停过程
但这也不是一个合理的要求。线程是否已经挂起自己或者即将挂起自己,只要它除了挂起自己之外别无选择,这又有什么关系呢
一个合理的需求应该通过让每个线程在某个地方设置一些指示来满足,即它已经接收到挂起请求并且即将停止执行。然后调用线程可以等待所有线程提供该指示。该要求不可能满足,但也没有意义。为了让线程传递它已挂起的事实,线程必须正在运行。无法确保线程已完成暂停过程
但这也不是一个合理的要求。线程是否已经挂起自己或者即将挂起自己,只要它除了挂起自己之外别无选择,这又有什么关系呢
一个合理的需求应该通过让每个线程在某个地方设置一些指示来满足,即它已经接收到挂起请求并且即将停止执行。然后调用线程可以等待所有线程提供该指示。任何并行解决方案的通用正确设计是定义令牌流和触发规则(请参阅Petry Net tedminology)。最简单和有用的触发规则是在所有输入令牌就绪时启动操作。在您的情况下,输入令牌隐藏在whle条件和suspend条件中。您的错误是将挂起条件定义为负值,而所有标记都必须定义为正值。也就是说,一个线程在有足够的令牌时工作,当令牌耗尽时停止,然后线程等待,同时外部线程增加令牌的数量
令牌可以有两种-黑色(纯权限),通过信号量传递,以及颜色(消息),通过阻塞队列传递。这两个通信器类涵盖了大多数用例。在某些复杂的情况下,用户可以使用synchronized/wait/notify创建自定义通信程序
因此,设计任何并行程序的标准方法如下:
- 设计Petry网,其中包含令牌(通信者)和转换(动作)的位置
- 将位置映射到信号量/阻塞队列/自定义通讯器,并转换到线程(或参与者)
任何并行解决方案的通用正确设计是定义令牌流和触发规则(参见Petry Net tedminology)。最简单和有用的触发规则是在所有输入令牌就绪时启动操作。在您的情况下,输入令牌隐藏在whle条件和suspend条件中。您的错误是将挂起条件定义为负值,而所有标记都必须定义为正值。也就是说,一个线程在有足够的令牌时工作,当令牌耗尽时停止,然后线程等待,同时外部线程增加令牌的数量
令牌可以有两种-黑色(纯权限),通过信号量传递,以及颜色(消息),通过阻塞队列传递。这两个通信器类涵盖了大多数用例。在某些复杂的情况下,用户可以使用synchronized/wait/notify创建自定义通信程序
因此,设计任何并行程序的标准方法如下:
- 设计Petry网,其中包含令牌(通信者)和转换(动作)的位置
- 将位置映射到信号量/阻塞队列/自定义通讯器,并转换到线程(或参与者)
MakePawnThread#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();
}
}
使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();
}
}
谢谢你的解释!我试图按照您在第三段中所说的做,将CountDownLatch
传递给所有线程,但这不是一个很好的方法-它只能使用一次(不是循环的),而且一些线程可能会终止,因此固定大小的锁存器不适合这样做。你知道什么机制是最好的吗?@MikolajMGT为什么不计算一下运行的线程数?谢谢你的解释!我试图按照您在第三段中所说的做,将CountDownLatch
传递给所有线程,但这不是一个很好的方法-它只能使用一次(不是循环的),而且一些线程可能会终止,因此固定大小的锁存器不适合这样做。你知道哪种机制最好吗?@MikolajMGT为什么不计算一下运行的线程数呢?