我正在尝试使用等号通知使用2个线程生成奇数/偶数。
但它只是打印 1 。
以下是代码:
Even.java
public class Even implements Runnable {
private int i; private Object ob
public Even(int i,Object o) {
this.i=i;
this.ob=o;
}
@Override
public void run() {
while (true) {
synchronized (ob) {
while (i % 2 == 0) {
try {
ob.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
i++;
System.out.println(i);
ob.notifyAll();
}
}
}
}
Odd.java
public class Odd implements Runnable {
private int i; private Object ob;
public Odd(int i) {
this.i=i;
this.ob=o;
}
@Override
public void run() {
while (true) {
synchronized (ob) {
while (i % 2 == 1) {
try {
ob.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
i++;
System.out.println(i);
ob.notifyAll();
}
}
}
}
Test.java
public class Test {
public static void main(String[] args) {
int i = 0;
Object lock = new Object();
Thread t1 = new Thread(new Even(i),lock);
Thread t2 = new Thread(new Odd(i),lock);
t1.start();
t2.start();
}
}
输出:
1
有谁能告诉我我犯了哪个错误?
答案 0 :(得分:1)
嗯,我认为这可能是一个问题,Integer似乎在Java中是不可变的。因此,如果两个类中的一个更改了i值(由于第一个输出为1,它显然会这样做),您将不再同步相同的对象。
因此,您更改i
中Odd
中存储的值并在新对象上调用notifyAll
,因为您在一个对象上调用notifyAll
,所以Java会抱怨实际上从未被锁定过。
答案 1 :(得分:1)
线程已进入 INDEFINITE WAITING 状态。请注意(t1
,即Even
和t2
的实例,即Odd
的实例),每个都有一个实例变量i
的单独副本。
以下是幕后的内容:
t1
(Even
)和t2
(Odd
)都在 READY 中,然后 RUNNABLE 州t1
被安排并让处理器执行。 t1
进入 RUNNING 状态。调用t1
的{{1}}
run()
成功并且控制进入外部循环while(true)
synchronized(ob)
锁定了对象t1
ob
最初是i
; 0
条件评估为i % 2 == 0
true
阻止并调用try
ob.wait();
进入等待状态并等待有人通知对象t1
ob
被安排并让处理器执行。 t2
进入 RUNNING 状态
t2
成功并且控制进入外部循环while(true)
synchronized(ob)
锁定了对象t2
ob
最初为i
(请记住先前增加的0
是i
- t1
。Even
的本地值。此上下文中的变量i
是i
的本地变量,即t2
。Odd
。i
评估为i % 2 == 1
,控件跳过内部循环false
i++;
从i
增加到0
1
将System.out.println(i);
打印到控制台1
并通知所有等待对象ob.notifyAll();
(在我们的例子中为ob
)的线程t1
和t1
再次回到 RUNNABLE 状态t2
被安排并让处理器执行
t1
从之前离开的位置恢复其操作(即t1
之后的声明)ob.wait();
并且由于没有异常,因此跳过并控制回到catch (InterruptedException e)
检查while ( i % 2 == 0)
的{{1}}(即t1
。i
)仍为Even
,因为控件无法到达{{1}行} i
类0
评估为i++;
并且控件进入while循环体,然后进入try块并调用Even
i % 2 == 0
再次进入等待状态并等待有人通知对象ob true
被安排并让处理器执行。 ob.wait();
进入 RUNNING 状态
t1
从之前离开的位置恢复其操作(即t2
之后的声明)t2
之后没有其他语句,所以控件到达外部while循环t2
被评估并且控制进入外部循环体ob.notifyAll();
ob.notifyAll();
锁定了对象while(true)
synchronized(ob)
的{{1}}现在是t1
,因为它先前已增加并打印在控制台上ob
被评估为t2
并且控件进入内部循环体,然后i
阻止并调用1
while ( i % 2 == 1)
进入等待状态true
和try
现在都处于 WAITING 状态;等待对象ob.wait();
。等待某人通过对象t2
通知他们。可悲的是,没有人可以拯救因此 INDEFINITE WAITING
以下代码应该有助于您实现的目标
t1
注意:强>
t2
对象现在是ob
和ob
个线程之间的共享实例。答案 2 :(得分:0)
您可以将i变量作为类属性放入Test类中,然后创建一个Test类型的对象,并将其作为参数传递给构造函数:
public class Test {
public Integer i = 0;
public static void main(String[] args) {
Object o = new Object();
Test t = new Test();
Thread t1 = new Thread(new Even(t,o));
Thread t2 = new Thread(new Odd(t,o));
t1.start();
t2.start();
}
}
public class Even implements Runnable {
private Test t;
private Object o;
public Even(Test t, Object o) {
this.t=t;
this.o=o;
}
@Override
public void run() {
while (true) {
synchronized (o) {
while (t.i % 2 == 0) {
try {
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
t.i++;
System.out.println(t.i);
o.notifyAll();
}
}
}
}
public class Odd implements Runnable {
private Test t;
private Object o;
public Odd(Test t, Object o) {
this.t=t;
this.o = o;
}
@Override
public void run() {
while (true) {
synchronized (o) {
while (t.i % 2 == 1) {
try {
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
t.i++;
System.out.println(t.i);
o.notifyAll();
}
}
}
}