【问题标题】:Problems suspending and resuming a thread暂停和恢复线程的问题
【发布时间】:2021-12-27 12:03:04
【问题描述】:

我是 Java 编程新手,第一次尝试线程。我创建了一个线程来打印偶数,然后创建一个线程来打印奇数。在奇数线程中,在要打印的数字 11 之后有一个 suspend() 方法。之后还有另一个块打印从 13 到 21 的奇数。在 main() 中,我加入了 2 个线程。在主函数中,我首先调用线程 t1(偶数),然后将其与线程 t2(奇数)连接。由于有暂停,它会在打印 11 后暂停输出,它会在那里暂停。但在那之后,当我调用 t2.resume() 时,它不会继续打印 13 到 21。为什么它不打印其余部分?我怎样才能让它恢复?

这是代码,请看:

class Hi extends Thread {
    
    public void run(){
        try{
            for(int i=0; i<=10; i++){
                System.out.println("1st: " + (2*i));
                sleep(100);
            }
        } catch(Exception e){
            System.out.println(e);
        }
    }
}
class Hello extends Thread {
    
    public void run(){
        try {
            for(int i=0; i<=5; i++){
                System.out.println("2nd: " +(2*i+1));
            }
            suspend();
            System.out.println(" Resumes again");
            for(int i=6; i<=10; i++){
                System.out.println("2nd: " +(2*i+1));
            }
        } catch(Exception e){
            System.out.println(e);
        }     
    }
}
public class thread {

    public static void main(String args[]){
        Hi t1 = new Hi();
        Hello t2 = new Hello();
        t1.start();
        try {
            t1.join();
        } catch (Exception e) {
        }
        t2.start();
        t2.resume();
    }
}   

输出:

1st: 0
1st: 2
1st: 4
1st: 6
1st: 8
1st: 10
1st: 12
1st: 14
1st: 16
1st: 18
1st: 20
Exception in thread "main" 2nd: 1
java.lang.IllegalThreadStateException
2nd: 3
2nd: 5
2nd: 7
2nd: 9
2nd: 11
    at java.base/java.lang.Thread.start(Thread.java:791)
    at thread.main(thread.java:57)

在此之后程序没有退出,我必须在终端中手动(ctrl+c)退出程序。

【问题讨论】:

  • 为什么要发布格式不正确的代码?理解别人的代码已经够难的了,为什么要让它变得更难呢?
  • 另外,suspend() 已被弃用——因此您不应调用此方法(或扩展 Thread,就此而言)。
  • 一开始的那堵文字墙也是如此。您希望其他人花时间帮助您,所以请尽可能简单:编写易于消化的内容。
  • 然后:空的 catch 块是一个非常糟糕的做法。永远不要那样做,至少打印异常。否则,您只是在抑制错误。
  • 请勿在任何情况下致电suspendresume。它们不能正常工作。阅读链接以了解原因。一般来说,Java 程序员必须养成阅读他们使用的方法的文档的习惯。线程之间的协作通常通过同步或 java.util.concurrent 中的类来完成。

标签: java multithreading exception


【解决方案1】:

当我调用 t2.resume() 时,它不会继续打印 13 到 21。为什么它不打印其余部分?

@Gray answered that one。 TLDR:您的主线程调用t2.resume() 您的t2 线程调用suspend()

我很想知道您使用的是什么 Java 版本。我在 macOS 上运行 JDK11,我无法让您的程序抛出 java.lang.IllegalThreadStateException,但我猜测在您使用的任何 JDK 和操作系统中,当您的主线程尝试 resume() 未挂起的线程。


我怎样才能让它恢复?

我会使用Semaphore

t2 线程等待来自信号量的acquire() 一个“许可”,并让主线程release() 在适当的时间允许该信号量。

我更改了您的程序,将suspend() 调用替换为sem.acquire(),并将resume() 调用替换为sem.release()。在释放信号量之前,我还在主线程中调用了Thread.sleep(),以便您可以看到暂停。以下是全部内容:

import java.util.concurrent.Semaphore;

class Hi extends Thread {   // This class is exactly the same as yours.
    public void run(){
        try{
            for(int i=0; i<=10; i++){
                System.out.println("1st: " + (2*i));
                sleep(100);
            }
        } catch(Exception e){
            System.out.println(e);
        }
    }
}
class Hello extends Thread {

    private Semaphore sem;          // new member variable

    public Hello(Semaphore sem) {   // new constructor to set the new member.
        this.sem = sem;
    }
    
    public void run(){
        try {
            for(int i=0; i<=5; i++){
                System.out.println("2nd: " +(2*i+1));
            }

            // Wait to "acquire" a permit from the semaphore. This won't
            //  return until the main thread "releases" a permit _to_ the
            //  semaphore.
            sem.acquire();

            System.out.println(" Resumes again");
            for(int i=6; i<=10; i++){
                System.out.println("2nd: " +(2*i+1));
            }
        } catch(Exception e){
            System.out.println(e);
        }     
    }
}

public class Wonk {

    public static void main(String args[]){

        // Create a semaphore that initially has 0 "permits."
        Semaphore s = new Semaphore(0);

        Hi t1 = new Hi();
        Hello t2 = new Hello(s);
        t1.start();
        try {
            t1.join();
        } catch (Exception e) {
        }

        t2.start();
        waitabit(5000);  // Sleep 5 seconds, and then...
        s.release();     // ...Release the Hounds!!
    }

    private static void waitabit(int numMilliseconds) {
        try {
            Thread.sleep(numMilliseconds);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

【讨论】:

    【解决方案2】:

    在此之后程序没有退出,我必须在终端中手动退出程序(ctrl+c)。

    除了您不应该使用suspend()resume() 之外,我相信您的问题是典型的竞争条件。主线程调用t2.start(),然后调用t2.resume(),但线程尚未挂起。主线程抛出异常,然后t2 线程完成打印并调用suspend()。没有人可以继续它,所以线程永远不会结束,你必须控制-c你的程序。

    您应该对示例代码进行一些更改。首先使用等待/通知而不是suspend()。例如,让Hello 线程执行:

    // t2 thread shouldn't use suspend which is deprecated for good reason
    synchronized (this) {
        wait();
    }
    

    然后在主线程中,您需要循环直到线程处于等待状态,然后再通知它。比如:

    while (true) {
        if (t2.getState() == State.WAITING) {
            synchronized (t2) {
                t2.notify();
            }
            break;
        }
        Thread.sleep(100);
    }
    

    这些线程练习,其中一个线程与另一个线程一起被锁定(除了最后的join())是线程 IMO 的一个坏例子。

    耦合其他 cmets。您应该在代码末尾使用join()Hello 线程,如果可以提供帮助,请不要捕获Exception,因为它隐藏了可能是问题根源的异常(至少按照@GhostCat 的建议打印它们),并且当你捕捉到InterruptedException时总是会重新中断你的线程:

    try {
        t2.join();
    } catch (InterruptedException e) {
        // always a good pattern
        Thread.currentThread().interrupt();
        e.printStackTrace();
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-24
      • 1970-01-01
      • 1970-01-01
      • 2010-12-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多