调用 notifyAll() 后线程未唤醒

Ant*_*nus 1 java concurrency multithreading

问题是创建 3 个线程,一个每秒打印一个随机数,如果数字是偶数,则第二个线程将其平方,如果是奇数,则第三个线程将其立方。这应该发生给定的次数(在我的代码中它是无限的,稍后将对其进行编辑)。我的问题是,在第一次迭代后(即创建一个随机数,正确的线程唤醒并执行其操作),第二个/第三个线程在再次调用 notifyAll() 后不会唤醒。我的代码如下所示,并带有示例输出。我添加了一些用于调试的打印语句:

package com.company;
import java.util.*;


class RandomNumber implements Runnable{

int randomNum = 0;
Random rand = new Random();
boolean flag = false;

public RandomNumber() {
    Thread newThread = new Thread(this,"Random Number");
    newThread.start();
}

@Override
public synchronized void run()
{

    while(flag == false) {
        System.out.println("random num thread");
        try {
            randomNum = rand.nextInt(100) + 1;
            System.out.println(randomNum);
            flag = true;
            notifyAll();
            //System.out.println(flag);
            Thread.sleep(1000);

        } catch (Exception e) {
            System.out.println("Exception Caught");
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

}

class SquareNumber implements Runnable{

 RandomNumber randomNumOb;

public SquareNumber(RandomNumber randNumObject){

    this.randomNumOb = randNumObject;
    Thread squareThread = new Thread(this, "Square thread");
    squareThread.start();

}

@Override
public synchronized void run() {

    System.out.println("square thread before while");

    while(randomNumOb.flag == true) {
         System.out.println("square thread");
        if (randomNumOb.randomNum % 2 == 0)
        {
            System.out.println("Number is even so square of " + randomNumOb.randomNum + " is: " + (randomNumOb.randomNum * randomNumOb.randomNum));

            try {
                randomNumOb.flag = false;
                wait();
            }catch(Exception e){
                System.out.println("Exception caught");
            }
        }

        else {
            try {
                System.out.println("inside square else");
                wait();
            } catch (Exception e) {
                System.out.println("Exception Caught");
            }

        }
    }
    System.out.println("square thread after while");
}
Run Code Online (Sandbox Code Playgroud)

}

class CubeNumber implements Runnable{

RandomNumber randomNumOb;

public CubeNumber(RandomNumber randNumObject){

    this.randomNumOb = randNumObject;
    Thread squareThread = new Thread(this, "Square thread");
    squareThread.start();

}

@Override
public synchronized void run() {
    System.out.println("cube thread before while");

    while(randomNumOb.flag == true) {
        System.out.println("cube thread");
        if (randomNumOb.randomNum % 2 == 1) {
            System.out.println("Number is odd so cube of " + randomNumOb.randomNum + " is: " + (randomNumOb.randomNum * randomNumOb.randomNum * randomNumOb.randomNum));
            try {
                randomNumOb.flag = false;
                wait();
            }catch (Exception e){

            }
        }

        else {
            try {
                System.out.println("inside cube else");
                //randomNumOb.flag = false;
                wait();
            } catch (Exception e) {
                System.out.println("Exception Caught");
            }

        }
    }
    System.out.println("cube thread after while");

}
Run Code Online (Sandbox Code Playgroud)

}

public class Main {

public static void main(String[] args) {

    RandomNumber random = new RandomNumber();
    SquareNumber square = new SquareNumber(random);
    CubeNumber cube = new CubeNumber(random);
}
Run Code Online (Sandbox Code Playgroud)

}

示例输出:

random num thread
81
square thread before while
square thread
inside square else
cube thread before while
cube thread
Number is odd so cube of 81 is: 531441
random num thread
68
Run Code Online (Sandbox Code Playgroud)

在此之后,方形或立方体线程似乎都没有醒来,并且无法弄清楚原因。任何帮助,将不胜感激。

Nat*_*hes 5

为了锁定和等待/通知工作,需要一个共享锁。

每个对象都有一个“内在锁”。该锁用作等待和通知的通信中心。将synchronized放在实例方法上意味着调用该方法的线程在进入该方法时获取该实例的内在锁,并在离开时释放该内在锁。wait/notify/notifyAll 方法只能由持有内在锁的线程调用。

当线程调用 wait 时,它会释放锁并且线程进入休眠状态,直到它收到通知(或被中断)。该锁跟踪当前正在等待它的线程,这称为等待集。

当一个线程调用 notify 时,它会告诉调度程序从锁的等待集中选择一个线程并向它发送通知。notifyAll 方法是相同的,除了它唤醒等待集中的所有其他线程。

这就是锁定如何确定哪个等待线程得到通知。

所以在贴出的代码中,这些 Runnable 中的每一个都获得了自己的内在锁,并且没有共享。唤醒通知必须由另一个线程引起,该线程已获取等待线程调用 wait on 的锁。

在这里,您可以在入口点类中创建一个公共锁

final Object lock = new Object();  // value referenced by lock must not change
Run Code Online (Sandbox Code Playgroud)

并将其传递到构造函数中的不同 Runnables 中,例如:

public SquareNumber(RandomNumber randNumObject, Object lock){ 
    this.lock = lock;
    ...
Run Code Online (Sandbox Code Playgroud)

所以他们使用相同的锁。然后更改wait 和notify 方法调用以使用该共享锁对象,并将synchronized 方法更改为传入锁的synchronized 块。

顺便说一句,关于添加到 RandomNumber runnable 的睡眠:notifyAll 在当前线程释放锁之前不会生效(因为每个等待线程必须获取锁才能离开等待方法)。睡在这里不会给通知时间做任何事情,它只是防止任何事情发生。