调用notifyAll()后线程未唤醒

时间:2019-12-11 15:19:34

标签: java multithreading concurrency

问题是创建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");
        }

    }
}

}

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");
}

}

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");

}

}

public class Main {

public static void main(String[] args) {

    RandomNumber random = new RandomNumber();
    SquareNumber square = new SquareNumber(random);
    CubeNumber cube = new CubeNumber(random);
}

}

示例输出:

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

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

2 个答案:

答案 0 :(得分:5)

要进行锁定并等待/进行通知,必须有一个共享锁。

有一个“内在锁”,被烘烤到每个物体中。该锁用作等待和通知的通信中心。将同步放在实例方法上意味着,调用该方法的线程在进入该方法时会获取该实例的固有锁定,并在离开该实例时释放该固有锁定。 wait / notify / notifyAll方法只能由持有内部锁的线程调用。

当线程调用等待时,它将释放锁定,并且线程将进入休眠状态,直到收到通知(或被中断)。该锁跟踪当前正在等待哪些线程,这就是等待集。

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

这就是锁定确定通知哪个等待线程的方式。

因此,在已发布的代码中,每个Runnable都获取自己的固有锁,并且没有共享。唤醒通知必须由另一个线程获得,该线程已获得等待线程调用wait的锁。

在这里您可以在入口点类中创建通用锁

final Object lock = new Object();  // value referenced by lock must not change

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

public SquareNumber(RandomNumber randNumObject, Object lock){ 
    this.lock = lock;
    ...

,所以他们使用相同的锁。然后,更改等待和通知方法调用以使用该共享锁对象,并将同步方法更改为传递了锁的同步块。

关于添加到RandomNumber可运行状态的睡眠的详细信息:notifyAll直到当前线程释放锁后才生效(因为每个等待线程都必须获取锁才能离开wait方法)。在这里睡觉并没有做任何事情来给通知时间上班,只是阻止了任何事情的发生。

答案 1 :(得分:0)

CubeNumberSquareNumber都都等待它们自己的对象上的通知,而不是等待随机对象上的通知。因此他们永远不会收到通知。

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 void run()
{

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

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

    }

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;
                synchronised(randomNumOb) {
                    randomNumOb.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");

}

与方形版本相同。

但是,另一个问题是,您无法保证多维数据集和平方方法都将在randomnumber的睡眠秒内运行。 可能在这里使用后代。