java等待并通知
我取一个整数变量并与两个线程共享。一个线程应按顺序打印偶数,一个线程应按顺序打印奇数。 但是notify()抛出非法监视器状态异常java等待并通知,java,multithreading,wait,notify,Java,Multithreading,Wait,Notify,我取一个整数变量并与两个线程共享。一个线程应按顺序打印偶数,一个线程应按顺序打印奇数。 但是notify()抛出非法监视器状态异常 package mywaitnotifytest; public class App { public static void main(String[] args) { Integer i=0; Even even = new Even(i); even.setName("EvenThread");
package mywaitnotifytest;
public class App {
public static void main(String[] args) {
Integer i=0;
Even even = new Even(i);
even.setName("EvenThread");
Odd odd = new Odd(i);
odd.setName("OddThread");
even.start();
odd.start();
}
}
class Even extends Thread{
Integer var;
Even(Integer var){
this.var=var;
}
@Override
public void run() {
while(true){
synchronized (var) {
if(var%2==0){
try {
var.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
var++;
System.out.println(Thread.currentThread().getName()+" "+var);
var.notify();
}
}
}
}
class Odd extends Thread{
Integer var;
Odd(Integer var){
this.var=var;
}
@Override
public void run() {
while(true){
synchronized (var) {
if(var%2!=0){
try {
var.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
var++;
System.out.println(Thread.currentThread().getName()+" "+var);
var.notify();
}
}
}
}
输出为:
ODD线程1
Exception in thread "OddThread" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at mywaitnotifytest.Odd.run(App.java:67)
我认为这与通常的答案完全不同,可以给出另一个答案 在这种情况下,您使用的是
已同步的。应用锁定时,锁定对象不是参照
这将锁定对象var
引用,而不是将var
作为字段
这将替换指向的对象var
。这和
var = Integer.valueOf(var.intValue() + 1);
注意:Integer
实际上所有的原语包装都是不可变的。当您对它们执行任何操作时,实际上是在取消装箱,使用原语值进行计算并重新装箱对象。如果将同一对象合并,则可以将其取回。e、 g
Integer i = 10;
i += 0; // gives back the same object.
但是,如果对象未合并
Double d = 10;
d += 0; // creates a new object.
尝试对新对象(而不是锁定的对象)调用notify
你不应该试图锁定你变异的字段。它不会做它看起来做的事情。您也不应该锁定池对象。在这种情况下,您可以让另一个线程使用相同的整数
用于不相关的目的,并且notify()
将唤醒一个不相关的线程
要正确使用等待/通知,您应该
notify()
或notifyAll()
在另一个共享字段中的状态更改后
- 您应该对
wait()
使用while循环来检查状态更改
如果你不这样做
- 如果另一个线程未等待,通知可能会丢失
- 等待可能会错误地唤醒,即使没有调用notify
对于上述要求,规范中建议的编辑是什么?如何为多个线程共享同一对象
我没有使用整数包装类,而是创建了自己的类,现在它运行良好
package mywaitnotifytest;
public class App {
public static void main(String[] args) {
MyInt i = new MyInt(0);
Even even = new Even(i);
even.setName("EvenThread");
Odd odd = new Odd(i);
odd.setName("OddThread");
even.start();
odd.start();
}
}
class Even extends Thread {
MyInt var;
Even(MyInt var) {
this.var = var;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(200);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
synchronized (var) {
if (var.i % 2 == 0) {
try {
var.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
var.i++;
System.out.println(Thread.currentThread().getName() + " " + var.i);
var.notify();
}
}
}
}
class Odd extends Thread {
MyInt var;
Odd(MyInt var) {
this.var = var;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(2000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
synchronized (var) {
if (var.i % 2 != 0) {
try {
var.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
var.i++;
System.out.println(Thread.currentThread().getName() + " " + var.i);
var.notify();
}
}
}
}
class MyInt {
int i = 0;
public MyInt(int i) {
super();
this.i = i;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "" + i;
}
}
这个问题似乎有所不同,因为OP在通知
而不是等待
上出现异常。另外,异常的原因是完全不同的,因为与非同步的code无关,所以您没有在锁定的同一对象上调用notify()
。简而言之,不要锁定可变字段。当你改变它时,你就改变了它。也不要锁定池对象,因为Integer
是因为这会产生混乱的结果。我正在从同步块调用wait和notifyonly@ortis当OP使用synchornized
时,他/她没有对对象调用notify,他们已synchronized
@NareshMuthyala使用synchronized
意味着您必须调用通知
/在同一对象上等待
。它不会授予您锁定任何对象的权限。这就是为什么在处理同步的、等待
、或通知
@ortis(如果可能)时,您应该始终使用final
的原因。对于序列化对象,并不总是可以使用final
,但字段应该是有效的final+我回答了这个问题。解释应如何使用wait(在一段时间内(conditionNotMet){waitOnTheLockObject;})。这个答案应该被接受@PeterLawrey,还有,如果您编辑并明确提到原语的所有包装类都是不可变的,那就太好了。它将解释为什么Integer上的var++将(显式地)创建一个新的Integer对象。@Amudhan补充了更多的解释。发布一个解决方案而不解释原始问题的原因对读者来说是无用的。请接受以上答案。
Double d = 10;
d += 0; // creates a new object.
var.notify();
public class PingPong implements Runnable {
static class Shared { int num; }
private final Shared var;
private final int bit;
public static void main(String[] args) {
Shared var = new Shared();
new Thread(new PingPong(var, 0), "EvenThread").start();
new Thread(new PingPong(var, 1), "OddThread").start();
}
PingPong(Shared var, int bit) {
this.var = var;
this.bit = bit;
}
@Override
public void run() {
try {
String name = Thread.currentThread().getName();
while (true) {
synchronized (var) {
while (var.num % 2 == bit)
var.wait();
var.num++;
System.out.println(name + " " + var.num);
var.notify();
}
}
} catch (InterruptedException e) {
System.out.println("Interrupted");
}
}
}
package mywaitnotifytest;
public class App {
public static void main(String[] args) {
MyInt i = new MyInt(0);
Even even = new Even(i);
even.setName("EvenThread");
Odd odd = new Odd(i);
odd.setName("OddThread");
even.start();
odd.start();
}
}
class Even extends Thread {
MyInt var;
Even(MyInt var) {
this.var = var;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(200);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
synchronized (var) {
if (var.i % 2 == 0) {
try {
var.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
var.i++;
System.out.println(Thread.currentThread().getName() + " " + var.i);
var.notify();
}
}
}
}
class Odd extends Thread {
MyInt var;
Odd(MyInt var) {
this.var = var;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(2000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
synchronized (var) {
if (var.i % 2 != 0) {
try {
var.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
var.i++;
System.out.println(Thread.currentThread().getName() + " " + var.i);
var.notify();
}
}
}
}
class MyInt {
int i = 0;
public MyInt(int i) {
super();
this.i = i;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "" + i;
}
}