TimeCollectTime 2021-05-31 13:03 采纳率: 50%
浏览 106
已采纳

Java关于多线程轮次输出5个数将1-60打印到控制台的问题

主函数部分:
public class Demo {
    public static void main(String[] args) {
        //创建打印数字类的对象
        PrintNum pn = new PrintNum();


        //以pn为参数创建三个线程
        //分别命名为1号、2号、3号
        Thread t1 = new Thread(pn, "1号");
        Thread t2 = new Thread(pn, "2号");
        Thread t3 = new Thread(pn, "3号");

        //启动线程
        t1.start();
        t2.start();
        t3.start();

    }
}

PrintNum类部分:
public class PrintNum implements Runnable {
    //num表示第n个数
    private int num = 1;
    //state用来作为某一线程是否需要wait的标志
    private String state = "1号";
    //N用来选择线程1还是线程2还是线程3
    int N = 1;

    public int getN() {
        return N;
    }

    public void setN(int n) {
        N = n;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    @Override
    public void run() {
       while (true) {
           if(this.num<=60) {
               if (getN() == 1) {
                   printnum1();
                   setN(2);
               } else if (getN() == 2) {
                   printnum2();
                   setN(3);
               } else if (getN() == 3) {
                   printnum3();
                   setN(1);
               }
           }else{
                break;
            }
        }
    }



    //线程1的内容
    public synchronized void printnum1() {
        if(!(getState().equals("1号"))){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        int num;
        for (num=this.num; this.num <= num + 4; this.num++) {
            if (this.num <= 60) {
                System.out.println(Thread.currentThread().getName() + "线程:" + this.num);
            }
        }

        //将状态设置为线程2
        setState("2号");

        //唤醒线程
        notifyAll();
    }

    //线程2的内容
    public synchronized void printnum2(){
        if(!(getState().equals("2号"))){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        int num;
        for (num=this.num; this.num <= num + 4; this.num++) {
            if (this.num <= 60) {
                System.out.println(Thread.currentThread().getName() + "线程:" + this.num);
            }
        }

        //将状态设置为线程3
        setState("3号");

        //唤醒线程
        notifyAll();
    }

    //线程3的内容
    public synchronized void printnum3(){
        if(!(getState().equals("3号"))){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        int num;
        for (num=this.num; this.num <= num + 4; this.num++) {
            if (this.num <= 60) {
                System.out.println(Thread.currentThread().getName() + "线程:" + this.num);
            }
        }

        //将状态设置为线程1
        setState("1号");

        //唤醒线程
        notifyAll();
    }
}

1号线程:1
1号线程:2
1号线程:3
1号线程:4
1号线程:5
1号线程:6
1号线程:7
1号线程:8
1号线程:9
1号线程:10
1号线程:11
1号线程:12
1号线程:13
1号线程:14
1号线程:15
1号线程:16
1号线程:17
1号线程:18
1号线程:19
1号线程:20
1号线程:21
1号线程:22
1号线程:23
1号线程:24
1号线程:25
1号线程:26
1号线程:27
1号线程:28
1号线程:29
1号线程:30
1号线程:31
1号线程:32
1号线程:33
1号线程:34
1号线程:35
1号线程:36
1号线程:37
1号线程:38
1号线程:39
1号线程:40
1号线程:41
1号线程:42
1号线程:43
1号线程:44
1号线程:45
1号线程:46
1号线程:47
1号线程:48
1号线程:49
1号线程:50
1号线程:51
1号线程:52
1号线程:53
1号线程:54
1号线程:55
1号线程:56
1号线程:57
1号线程:58
1号线程:59
1号线程:60

结果输出的全是线程一的1-60,而且没有使进程停止。

我的理解是:
       一开始因为有int N = 1;所以run()里执行的是 if (getN() == 1) 的代码块,
里面则是调用的同步方法快printnum1(),因为是线程t1先开始所以应该由它抢到执行权,
当完成前5个打印语句后,将state改为"2号",N改为2,那么这个while循环应该要重新循环了,并且应该直接去if (getN() == 2)的代码块才对。
    此时却仍然是Thread.currentThread().getName()为1号。

    因此我第一个疑惑是为什么一直是1个线程在抢执行权,但是其他两个没有动? 

    而当最后一个for循环结束前应该也会有this.num++的动作才对,这样才可以在run()里执行break语句,可是并没有退出循环。

    因此我第二个疑惑是程序没有直接退结束,而是一直处于调试状态的原因究竟是为何呢?

 

请大神们给一个解决方法的思路,谢谢!

 

 

 

  • 写回答

5条回答 默认 最新

  • 影流之祖 2021-06-01 10:55
    关注

    引用题主这句话

    当完成前5个打印语句后,将state改为"2号",N改为2,那么这个while循环应该要重新循环了,并且应该直接去if (getN() == 2)的代码块才对。
        此时却仍然是Thread.currentThread().getName()为1号。

    这是有问题的,当n改为2,也只有1号线程开始第二轮循环,2号和3号线程还阻塞在printnum1方法那刚刚准备从阻塞切换为就绪状态呢,等这两个线程切换为就绪状态,cpu也给了时间片,可以重新抢占锁的时候  线程1已经挨个把60个数打印完了,这当然取决于电脑的运算速度,题主不妨把循环次数调大到几千,这个时候应该就能看到2号和3号线程打印数了,,,

    至于第二个问题,实际上你多运行几次可能就能正常退出了,假设有一次上下文切换给力了,在1号线程打印完5个数后,2号侥幸拿到了锁,进入了printnum1方法,但是发现state不是预期值,于是就wait了把锁让了出来  1号和3号再次抢占锁,很不幸1号没抢到(如果1号抢到了锁就会继续打印,因为state的值全是1号线程在改变,所以他但凡抢到锁就能畅通无阻打印),3号抢到了,和2号的结局一样,state不是它想要的,继续wait,此时1号线程在等着抢占锁的队列,而2号刚刚被3号唤醒,正准备跻身1号线程所在队列,相比1号失去先机,1号继续打印,等到1号打印完,再次唤醒他们,这个时候num已经61了,2号3号相继灰溜溜退出printnum1方法,这样就能正常退出方法了,题主说的没有退出方法的时候,多半是2号和3号在1号打印完60个数前根本没获取到锁,等到1号已经退出while循环,2号进入printnum1,然后wait,3号也进入printnum1,继续wait,两个线程就wait在哪了,也没有进程去唤醒他们了,所以持续就永远被这两个线程托在那里了

    如果要验证,题主不妨在run方法break前面打印一下,再在printnum1方法第一句打印谁抢到了锁

    上述均为个人理解,有误请帮忙勘误哈哈哈

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(4条)

报告相同问题?

悬赏问题

  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 信号傅里叶变换在matlab上遇到的小问题请求帮助
  • ¥15 保护模式-系统加载-段寄存器
  • ¥15 电脑桌面设定一个区域禁止鼠标操作