[英]Threads not waking up after notifyAll() is called
問題是創建 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
在此之后,方形或立方體線程似乎都沒有醒來,並且無法弄清楚原因。 任何幫助,將不勝感激。
為了鎖定和等待/通知工作,需要一個共享鎖。
每個對象都有一個“內在鎖”。 該鎖用作等待和通知的通信中心。 將synchronized放在實例方法上意味着調用該方法的線程在進入該方法時獲取該實例的內在鎖,並在離開時釋放該內在鎖。 wait/notify/notifyAll 方法只能由持有內在鎖的線程調用。
當線程調用 wait 時,它會釋放鎖並且線程進入休眠狀態,直到它收到通知(或被中斷)。 該鎖跟蹤當前正在等待它的線程,這稱為等待集。
當一個線程調用 notify 時,它會告訴調度程序從鎖的等待集中選擇一個線程並向它發送通知。 notifyAll 方法是相同的,除了它喚醒等待集中的所有其他線程。
這就是鎖定如何確定哪個等待線程得到通知。
所以在貼出的代碼中,這些 Runnable 中的每一個都獲得了自己的內在鎖,並且沒有共享。 喚醒通知必須由另一個線程引起,該線程已獲取等待線程調用 wait on 的鎖。
在這里,您可以在入口點類中創建一個公共鎖
final Object lock = new Object(); // value referenced by lock must not change
並將其傳遞到構造函數中的不同 Runnables 中,例如:
public SquareNumber(RandomNumber randNumObject, Object lock){
this.lock = lock;
...
所以他們使用相同的鎖。 然后更改wait 和notify 方法調用以使用該共享鎖對象,並將synchronized 方法更改為傳入鎖的synchronized 塊。
順便說一句,關於添加到 RandomNumber runnable 的睡眠:notifyAll 在當前線程釋放鎖之前不會生效(因為每個等待線程必須獲取鎖才能離開等待方法)。 睡在這里不會給通知時間做任何事情,它只是防止任何事情發生。
CubeNumber
和SquareNumber
都等待它們自己對象的通知——而不是隨機對象的通知。 所以他們永遠不會得到通知。
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 的 sleep 秒內運行。 可能在這里使用 sempahor。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.