调用 notifyAll() 后未唤醒的线程



问题是创建 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

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

要使锁定和等待/通知正常工作,需要共享锁。

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

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

当线程调用 通知时,它会告诉调度程序从锁的等待集中选择一个线程并向其发送通知。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;
...

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

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

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

与方形版本相同。

但增加的问题是,您无法保证立方体和平方方法都将在随机数的睡眠秒内运行。 也许一个森帕霍尔在这里可能会有用。

最新更新