【问题标题】:how to pause a thread and resume it exactly where it was left off如何暂停一个线程并在它停止的地方恢复它
【发布时间】:2015-10-29 15:12:38
【问题描述】:

我正在尝试提供一种解决方案,让线程可以暂停并准确地从中断的地方恢复。

所以这是一个模拟我的问题的示例代码:2 个线程在后台运行:taskThreadbusyThread。当busyThread 处于系统繁忙区域 时,taskThread 必须立即 alt/暂停并准确地从中断处恢复。例如,如果taskThread 在任务 C(已完成)处暂停,它应该在 D 处继续。

我尝试使用等待,在 taskThread 上通知但没有成功。

public class Test
{ 
   private Thread taskThread;
   private Thread busyThread;

public static void main(String args[]) throws Exception
{ 
    Test t = new Test();
    t.runTaskThread();
    t.runBusyThread(); 
} 


public void runTaskThread()
{
    taskThread = new Thread(new Runnable(){

        @Override
        public void run()
        {
            for (int x=0; x<100; x++)
            {
                try
                {                       
                    System.out.println("I'm doing task A for process #"+x);
                    Thread.sleep(1000);

                    System.out.println("I'm doing task B for process #"+x);
                    Thread.sleep(200);

                    System.out.println("I'm doing task C for process #"+x);
                    Thread.sleep(300);

                    System.out.println("I'm doing task D for process #"+x);
                    Thread.sleep(800);

                    System.out.println("\n\n");

                } catch (InterruptedException e)
                {                       
                    e.printStackTrace();
                }                                       
            }

        }});

    taskThread.start();
}

public void runBusyThread()
{
    busyThread = new Thread(new Runnable(){

        @Override
        public void run()
        {
            while (true)
            {
                Random rand = new Random();
                int randomNum = rand.nextInt(1000);
                if (randomNum<400)
                {
                    System.out.println("Wait...system is busy!!!");                     
                    try
                    {       //what should come here to to signal taskThread to paused
                            Thread.sleep(3000);

                             //what should come here to to signal taskThread to resume

                    } catch (InterruptedException e)
                    { 
                    }
                } else
                {
                    try
                    {
                        Thread.sleep(300);
                    } catch (InterruptedException e)
                    {
                    }   
                }                       
            }               
        }});    

    busyThread.start();
}
}

【问题讨论】:

  • 只是一个简单的问题,你想停止一个线程然后从它的左右开始吗?
  • 好点。不,我希望 alt/暂停(让我更改标题)谢谢!
  • 如果你在线程需要停止时杀死线程并在需要恢复时创建一个新线程会发生什么,这会是一个问题吗?
  • 是的,它必须从中断的地方继续;在现实世界的问题中,它转化为浪费时间
  • 忙线程准备忙,但任务线程正在执行任务时会发生什么?

标签: java multithreading concurrency wait notify


【解决方案1】:

并发包中有两个非常有用的类——CountDownLatchCyclicBarrier。如果您只需要此行为一次,您可能需要第一个(因为它无法重置)。

线程 1 将等待直到线程 2 通知。一旦倒计时到 0,线程 1 将永远不会在 await() 再次阻塞:

CountDownLatch cdl = new CountDownLatch(1);

// thread 1:
cdl.await();

// thread 2:
cdl.countDown();

线程将阻塞在await(),直到正好有两个线程在等待:

CyclicBarrier barrier = new CyclicBarrier(2);

// both threads:
barrier.await();

编辑:

这是我在修改代码时想到的,但我不清楚这是否是预期的行为。

注意 CountDownLatch 上的 volatile 关键字 - 这在这里非常重要,否则 taskThread 可能会缓存初始对象 (new CountDownLatch(0)),因此永远不会阻塞。

public class Test {

    private Thread taskThread;
    private Thread busyThread;

    private volatile CountDownLatch cdl = new CountDownLatch(0);

    public static void main(String args[]) throws Exception {
        Test t = new Test();
        t.runTaskThread();
        t.runBusyThread();
    }

    public void runTaskThread() {
        taskThread = new Thread(() -> {
            for (int x = 0; x < 100; x++) {
                waitIfSystemBusy();
                System.out.println("I'm doing task A for process #" + x);
                sleep(1000);

                waitIfSystemBusy();
                System.out.println("I'm doing task B for process #" + x);
                sleep(200);

                waitIfSystemBusy();
                System.out.println("I'm doing task C for process #" + x);
                sleep(300);

                waitIfSystemBusy();
                System.out.println("I'm doing task D for process #" + x);
                sleep(800);

                System.out.println("\n\n");
            }
        });

        taskThread.start();
    }

    public void runBusyThread() {
        busyThread = new Thread(() -> {
            while (true) {
                Random rand = new Random();
                int randomNum = rand.nextInt(1000);
                if (randomNum < 400) {
                    System.out.println("Wait...system is busy!!!");
                    cdl = new CountDownLatch(1); // signal taskThread to pause
                    sleep(3000);
                    cdl.countDown(); // signal taskThread to resume
                } else {
                    sleep(300);
                }
            }
        });

        busyThread.start();
    }

    private void waitIfSystemBusy() {
        try {
            cdl.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private static void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

}

【讨论】:

  • 感谢贾索劳;问题:如何保证taskThread在B时,busy结束后恢复到C?
  • @adhg “在 B”和“将恢复到 C”是什么意思?
  • 看下面的代码,taskThread有一个for循环,里面有tasks (System.out...A...B...C) 所以当taskThread在B暂停时应该从它离开的点继续前进并移动到C(不是A)。谢谢!
  • @adhg 我试了一下你的代码并编辑了我的答案。这是你需要的吗?
  • Jaroslaw,我最终使用了具有相同设计的信号量(互斥体)(在每个块之后检查 waifIfSysBusy)。感谢您的回答。
【解决方案2】:

这将使用已弃用的方法Thread.suspend/resume 来完成。 它们已被弃用,因为它们容易死锁,而像锁这样的并发机制以设计明确的方式运行(但仍然容易死锁)。

【讨论】:

  • mmmm,我虽然知道,但意识到它们已经“旧”了。如果是这样,您将如何使用它(代码在哪里?)
  • 在忙线程中:...; taskThread.suspend(); heavyWork(); taskThread.resume();。请注意,我从未使用过它们,因为我的线程彼此更好。
  • 如果你必须使用恢复/挂起,你的多线程应用程序设计得非常糟糕,它还会有一些其他错误。这些东西被弃用是有充分理由的——没有理智的理由去使用它们。老实说,这些函数的唯一好处是它们通过代码审查简化了我的生活:我可以拒绝所有使用这些函数的代码,而无需花费额外的时间(是的,使用它们就是那么糟糕)
【解决方案3】:

我建议创建一个implements Runnable 的类,它只跟踪您所在的stages

仅作为示例(请相应更改)

class MyRunnable implements Runnable {
    private int stage = 0; // if you want it gloabally, then use static
    @Override
    public void run() {
        try{
            switch(stage){
                case 1:
                    System.out.println("1");
                    stage++;

                case 2:
                    System.out.println("2");
                    Thread.sleep(2000);
                    stage++;
                default:
                    stage = 0;

            }
        }catch (Exception e){

        }
    }

}

现在要使用这样的类你只需要创建一个新线程 例如:

public static void main(String[] args) throws Exception{

  MyRunnable myRunnable=new MyRunnable();
  new Thread(myRunnable).start(); //it prints 1

  Thread.sleep(1000);

  new Thread(myRunnable).start(); //prints 2 follow by 2 sec sleep

}

注意:

此示例并非旨在准确回答问题,而是展示如何完成此操作的逻辑。

编辑 1:

这里应该用什么来表示 taskThread 暂停

taskThread.interupt();

这里应该发出什么信号来通知 taskThread 恢复

taskThread=new Thread(myRunnable);
taskThread.start();

【讨论】:

  • 感谢 nafas 的想法,我认为这种设计会增加对我的代码的维护,而不是使用暂停/继续机制(假设存在)来解决它。如果没有这样的机制,我更有可能接受这个想法。谢谢!
  • @adhg 我发现这个链接可能就是你要找的伙伴:stackoverflow.com/questions/11989589/…
  • 我看到了这个,但它实际上并没有从它离开的地方继续。从头开始。
  • @adhg 好吧,伙计,我没牌了,让我知道你是否找到更简单的方法,伙计,我也想知道答案
【解决方案4】:

我更喜欢 wait() 和 notifyAll(),而不是 sleep()。 有一个布尔 systemBusy,实现 get 和 set 方法; 现在在thread1中

    run(){
       synchronize(something){
          while(isSystemBusy()){
             try{
                wait();}
             catch{}
          }
       }
    }

在另一个线程上

        run(){
           setSystemBusy(true);
           //piece of code
           //task finished
           notifyAll();
           setSystemBusy(false);
        }

您可以在多个等待线程中使用它,只需记住在全部通知后设置适当的 while 条件为 false。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多