java等待并通知

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

我取一个整数变量并与两个线程共享。一个线程应按顺序打印偶数,一个线程应按顺序打印奇数。 但是notify()抛出非法监视器状态异常

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

}