package test;public class test { /**
 * @param args
 */
public static void main(String[] args) {
// TODO Auto-generated method stub final fun ff = new fun(); new Thread(new Runnable() { @Override
public void run() {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} ff.a(); }
}).start(); new Thread(new Runnable() { @Override
public void run() {
ff.b();
this.notifyAll(); }
}).start(); }}class fun { public void a() { for (int i = 0; i < 100; i++)
System.out.println("this  is A:" + i);
} public void b() { for (int i = 0; i < 100; i++)
System.out.println("this  is B:" + i);
}}报错Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:485)
at test.test$1.run(test.java:18)
at java.lang.Thread.run(Thread.java:680)Exception in thread "Thread-2" java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)
at test.test$2.run(test.java:38)
at java.lang.Thread.run(Thread.java:680)

解决方案 »

  1.   

    wait 和notify 之类的必须都放在进程同步块或者同步方法中。用synchronized 关键字。
      

  2.   

    又看到了熟悉的wait()、notify()……线程啥的我最在行了
      

  3.   

    问题出在这里:
    this.wait();
    this.notify();
    this的实例找不到。注意,这里的this不是指的线程,而是指某个外部类Test的实例对象。通过这个实例对象对线程进行操控。
      

  4.   


    借用一下个人浅显的觉得.
    new Thread(new Runnable){
    };
    这样的方式不太好最大的问题在this的时候找不到实例,建议还是取个名字吧。
      

  5.   

    哦……貌似是我有些搞错了……
    this不是外部类的对象,而是指synchronized的范围的对象。在使用wait()是使线程释放同步锁,并线程挂起;notify()是唤醒线程,但不会释放同步锁。
    所以这里必须考虑到一个“同步锁”和“synchronized范围”的问题。
    可以认为,不同的synchronized范围具有不同的锁。先贴一个能正常运转的程序:
    public class Test {
    /**
     * @param args
     */
    public static void main(String[] args) {
    final Fun ff = new Fun();
    new Thread(new Runnable() {
    String name = "aThread"; public void run() {
    synchronized (Test.class) {
    //注意这里是将语句都放到Test.class的领域中,并加上同步锁。要是把wait的语句注释掉,可以看到是先运行ff.a(),之后再运行ff.b()。
    //可以这么认为,虽然是写在两个不同的线程中,但运行的领域都在Test.class中。
    try {
    System.out.println(this.name);

    Test.class.wait();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("恢复线程");
    ff.a();
    }
    }
    }).start(); new Thread(new Runnable() {
    String name = "bThread";
    public void run() {
    synchronized (Test.class) {
    ff.b();
    Test.class.notifyAll();
    }
    }
    }).start();
    }//可以这么认为,Test.class领域中的程序如下:
    //try {
    // System.out.println(this.name);
    //
    // Test.class.wait();
    //} catch (InterruptedException e) {
    // e.printStackTrace();
    //}
    //System.out.println("恢复线程");
    //ff.a();
    //
    //ff.b();
    //Test.class.notifyAll();
    //
    //这样这些程序才能具有“相同的同步锁”。wait()了之后,线程A释放了同步锁,线程b获取同步锁,执行程序。然后线程bnotifyAll(),才能唤醒线程A中的程序。}class Fun {
    public void a() {
    for (int i = 0; i < 100; i++) {
    System.out.println("this  is A:" + i);
    }
    }
    public void b() {
    for (int i = 0; i < 100; i++) {
    System.out.println("this  is B:" + i);
    }
    }
    }
    然后再来看看这个程序:public class Test {
    /**
     * @param args
     */
    public static void main(String[] args) {
    final Fun ff = new Fun();
    new Thread(new Runnable() {
    String name = "aThread";
    public synchronized void run() {
    try {
    System.out.println(this.name);
    this.wait();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("恢复线程");
    ff.a();
    }
    }).start(); new Thread(new Runnable() {
    String name = "bThread";
    public synchronized void run() {
    ff.b();
    this.notifyAll();
    }
    }).start();
    } public synchronized void control(Object o) { }
    }class Fun {
    public void a() {
    for (int i = 0; i < 100; i++) {
    System.out.println("this  is A:" + i);
    }
    } public void b() {
    for (int i = 0; i < 100; i++) {
    System.out.println("this  is B:" + i);
    }
    }
    }
    //运行之后可以看到线程a虽然wait()了,但b运行结束,a却没有被唤醒。
    //这是因为两个run()方法的synchronized领域不同,是两个不同的同步锁。
    //线程a执行wait之后,释放的同步锁只是线程a的run方法的。线程b执行notifyAll,想要唤醒的也只是与线程b的run方法同领域的线程。
      

  6.   


    再看下这个程序,就可以更了解wait()、notify()的使用方式了。public class Test {
    final Fun ff = new Fun();
    String name;

    public Test(String name) {
    this.name = name;
    new Thread(new AThread()).start();
    new Thread(new BThread()).start();
    } /**
     * @param args
     */
    public static void main(String[] args) {
    // 构建一个实例t,并给予名字“t”
    // 启动线程都写在t的构造方法中了,可知两个线程都在test的领域中运行。
    Test t = new Test("t");
    } private class AThread implements Runnable {
    String name = "aThread"; public void run() {
    // 将同步的程序放到实例对象t的领域中。
    synchronized (Test.this) {
    System.out.println(Test.this.name);
    try {
    // this指的是内部匿名类对象。
    // this.wait();
    // System.out.println(this.name);
    // 但此时同步锁,是加在实例对象t上面的
    // 因此必须通过Test.this来获取实例对象t。
    // 如此才能使线程挂起。
    System.out.println("线程A挂起");
    Test.this.wait();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("恢复线程");
    ff.a();
    }
    }
    } private class BThread implements Runnable {
    String name = "bThread"; public void run() {
    synchronized (Test.this) {
    ff.b();
    Test.this.notify();
    }
    }
    }
    }class Fun {
    public void a() {
    for (int i = 0; i < 100; i++) {
    System.out.println("this  is A:" + i);
    }
    } public void b() {
    for (int i = 0; i < 100; i++) {
    System.out.println("this  is B:" + i);
    }
    System.out.println("ff.b()运行完毕");
    }
    }
      

  7.   


    其实我对 synchronized  的理解是互斥 ,就是A线程正在使用synchronized保护的线程,B线程只有等待A线程执行完了才可以进去,,,同步这个怎么理解?
      

  8.   


    请问为什么非要用synchronized 括起来才可以?对 synchronized  的理解是互斥 ,就是A线程正在使用synchronized保护的线程,B线程只有等待A线程执行完了才可以进去,,,同步这个怎么理解?
      

  9.   


    请看我在7楼的回复。这涉及到synchronized的范围问题。
      

  10.   


    synchronized 翻译为“同步”,但其实其作用有两个:
    一是生成一个同步锁;二是确定同步锁的作用域。
    wait()、notify()都是对“同步锁”的操作,要是连同步锁都没建立,如何操作?
    再看下7楼回复中的第二个例子。
    那个例子中,有两个synchronized(也就是有两个同步锁)。
    线程A中有同步锁A,然后其wait()了。这时候只有同样使用同步锁A的程序(也就是在同一个synchronized域中)才能唤醒线程A。
    线程B中有同步锁B,然后其中有个notifyAll()方法。但那个notifyAll方法只能唤醒使用同步锁B的程序。7楼第一个例子,synchronized域都是Test.class,是一个同步锁,所以线程B才能唤醒线程A。
    而8楼的例子,synchronized域则是在对象t当中。
    Test.class的领域太大了,所有Test中的对象都会包含其中,不能添加多个同步锁,所以一般不推荐使用Test.class作为域。
    而使用实例对象t作为同步域,则要灵活得多。可以在一个class中添加多个实例,也就能添加多个同步域了。
      

  11.   

    此外,我记得我还回复过楼主在另一个帖子中的问题。在那个帖子中,我贴了一个很长的程序。
    那个程序中,我建了个方法countmoney(),而且在其上加了synchronized。
    线程A与线程B都会进入那个countmoney()方法中。这样就是让两个线程进入同一个synchronized域,使用同一个同步锁。在countmoney()方法中具有wait()和notify()方法,就能控制线程A与线程B了。
    synchronize作用域都是外部类的实例对象,所以那个程序用的是this。
      

  12.   


    帮我看看  下面这个  那里出错了,按你的意思,我自己定义了一个实例  还是出错
    package test;public class test { /**
     * @param args
     */

     static Object object  = new Object();

    public static void main(String[] args) {
    // TODO Auto-generated method stub final fun ff = new fun(); new Thread(new Runnable() { @Override
    public void run() {
    try {
    object.wait();
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } ff.a(); }
    }).start(); new Thread(new Runnable() { @Override
    public void run() {
    ff.b();
    object.notifyAll(); }
    }).start(); }}class fun { public synchronized void a() { for (int i = 0; i < 10; i++)
    System.out.println("this  is A:" + i);
    } public synchronized void b() { for (int i = 0; i < 10; i++)
    System.out.println("this  is B:" + i);
    }}
      

  13.   


    啊哈,我就是写了个类似的程序。请看:public class Test {
    final static Fun ff = new Fun();
        
        /**
         * @param args
         */
        public static void main(String[] args) {
        
            new Thread(new Runnable() {
                String name = "aThread";
                public void run() {
                 Test.methodA();
                }
            }).start();
     
            new Thread(new Runnable() {
                String name = "bThread";
                public void run() {
                 Test.methodB();
                }
            }).start();
        }
        
        //静态方法的synchronized都是加在class上的。
        public synchronized static void methodA() {
            try {             
                Test.class.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("恢复线程");
            ff.a();
        }    public synchronized static void methodB() {
            ff.b();
            Test.class.notifyAll();
        }
    }
     
    class Fun {
        public void a() {
            for (int i = 0; i < 100; i++) {
                System.out.println("this  is A:" + i);
            }
        }
        public void b() {
            for (int i = 0; i < 100; i++) {
                System.out.println("this  is B:" + i);
            }
        }
    }
    还有:public class Test1 {
    final Fun ff = new Fun();
    String name; public Test1(String name) {
    this.name = name;
    new Thread(new Runnable() {
    public void run() {
    methodA();
    }
    }).start();
    new Thread(new Runnable() {
    public void run() {
    methodB();
    }
    }).start();
    } /**
     * @param args
     */
    public static void main(String[] args) {
    // 构建一个实例t,并给予名字“t”
    // 启动线程都写在t的构造方法中了,可知两个线程都在test的领域中运行。
    Test1 t = new Test1("t");
    } //动态方法的synchronized都是加在实例对象t上的。
    //所以this.wait(),this都是指实例对象t。
    public synchronized void methodA() {
    try {
    this.wait();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("恢复线程");
    ff.a();
    } public synchronized void methodB() {
    ff.b();
    this.notifyAll();
    } private class Fun {
    public void a() {
    for (int i = 0; i < 100; i++) {
    System.out.println("this  is A:" + i);
    }
    } public void b() {
    for (int i = 0; i < 100; i++) {
    System.out.println("this  is B:" + i);
    }
    }
    }
    }
      

  14.   

    然后我把那个很罗嗦的程序改了下。
    这里我把那个countMoney()方法拆开了。public class CountMoney {
    // 为了保证money对象的线程安全,此处用了一个内部类Money。
    // 该内部类中的plusOrMinus方法加了同步锁。
    Money money = new Money(0); public static void main(String[] args) {
    CountMoney c = new CountMoney();
    c.startProgram();
    } public void startProgram() {
    new Thread(new MyThread1()).start();
    new Thread(new MyThread2()).start();
    } private class MyThread1 implements Runnable {
    boolean overFlag; public void run() {
    while (!overFlag) {
    try {
    // 注意,此处money是线程1、线程2都会操作的对象,两个线程可能同时进行操作。
    // 将money作为一个内部类包装起来,然后使用一个synchronize的方法操纵money增减,这样对money的操作就是同步的了。
    // money++;
    money.plusOrMinus(1);
    // 若getMoney()加了synchronized修饰,就会没有误差。
    // 写入没问题,读取存在误差。 只是显示出来有误差而已。
    // 因为set方法加了synchronized,money的写入是没问题的。
    // 读数据可不用同步,但写入数据最好能同步。
    System.out.println("m1增加money,money值为---"
    + money.getMoney());
    // 判断,看是否需要唤醒线程2。
    countmoney4Thread1();
    Thread.sleep(60);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }
    } private class MyThread2 implements Runnable {
    boolean overFlag; public void run() {
    while (!overFlag) {
    try {
    // 判断,看是否需要挂起线程m2。
    countmoney4Thread2();
    // money -= 2;
    money.plusOrMinus(-2);
    System.out.println("m2减少money,money值为---"
    + money.getMoney());
    Thread.sleep(60);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }
    } public synchronized void countmoney4Thread1() {
    // money>20时,唤醒m2,允许减少money。
    if (money.getMoney() > 20) {
    // 此处省略了this.
    // this指向main中的实例c。
    notify();
    System.out.println("-----唤醒m2-----");
    }
    } public synchronized void countmoney4Thread2() {
    // money < 10的时候,挂起线程m2。
    if (money.getMoney() < 10) {
    try {
    System.out.println("-----挂起m2-----");
    // 线程1、线程2,都是在实例c的范围内执行,因此默认直接指向实例c。
    // 前面实际省略了this.
    wait();
    // this.wait();
    // 打印this就知道意思了。
    // System.out.println(this);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    } private class Money {
    int money; public Money(int x) {
    this.money = x;
    } // 该方法没有同步,因此在读取的时候就会出现数值误差。
    // 之后再加同步锁,就可以发现读取也没有误差了。
    // 但读取加synchronized是很影响性能的。
    // public synchronized int getMoney() {
    public int getMoney() {
    return money;
    } // 该方法加了同步,能保证money对象能被同步操作。
    public synchronized void plusOrMinus(int x) {
    this.money += x;
    // 该数据是在同步方法内的,因下面这个打印语句的是没有任何问题的。
    // System.out.println("(正确数据)money值被修改为---" + this.getMoney());
    }
    }
    }
      

  15.   

    这个肯定不行,要搞清同步块与同步方法中谁被锁定了,同步方法是this(你的程序就是fun类的某一对象)被锁定了,你对object调用wait方法,首先通过同步块锁定object,然后才能调用wait方法释放线程对object的锁,同样的道理,只有获得同一对象object的锁,才能将在同一对象object上等待的线程唤醒应该是这样:
    synchronized(object) {
        object.wait();
    }
    ...
    synchronized(object) {
        object.notifyAll();
    }
      

  16.   


    楼主要把synchronize放到Fun里面是吧?程序如下:public class Test {
    /**
     * @param args
     */
    public static void main(String[] args) {
    final Fun ff = new Fun();
    new Thread(new Runnable() {
    public void run() {
    ff.a();
    }
    }).start(); new Thread(new Runnable() {
    public void run() {
    ff.b();
    }
    }).start();
    }
    }class Fun {
    //这里是动态方法上加synchronized,所以this指对象ff。
    public synchronized void a() {
    try {
    this.wait();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("恢复线程");
    for (int i = 0; i < 100; i++) {
    System.out.println("this  is A:" + i);
    }
    } public synchronized void b() {
    for (int i = 0; i < 100; i++) {
    System.out.println("this  is B:" + i);
    }
    this.notifyAll();
    }
    }public class Test {
    /**
     * @param args
     */
    public static void main(String[] args) {
    new Thread(new Runnable() {
    public void run() {
    Fun.a();
    }
    }).start(); new Thread(new Runnable() {
    public void run() {
    Fun.b();
    }
    }).start();
    }
    }class Fun {
    //这里是静态方法上加synchronized,所以要用Fun.class.wait()。
    public synchronized static void a() {
    try {
    Fun.class.wait();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println("恢复线程");
    for (int i = 0; i < 100; i++) {
    System.out.println("this  is A:" + i);
    }
    } public synchronized static void b() {
    for (int i = 0; i < 100; i++) {
    System.out.println("this  is B:" + i);
    }
    Fun.class.notifyAll();
    }
    }
      

  17.   

    你楼上的2个例子  第二个例子如果把    public synchronized static void b() 改成   public synchronized  void b()
    也必须用这个:Fun.class.notifyAll();  吗?
      

  18.   


    static是静态方法,静态方法的引用是放在栈里面的。不加static就是动态方法,动态方法的引用是放在堆里面的,必须通过实例才能执行。静态方法与动态方法的“域”是不同的,静态方法的锁是加在类上面的,也就是Fun.class;动态方法的锁是加在实例上的,也就是实例t或其他实例对象。静态方法与动态方法的锁,仍然不是同一个。
    在同一个A.class中的所有静态方法、以及定义在A.class中的程序,在使用synchronize时有且只共享一个同步锁。
    实例对象,不仅包括了属性,还可以认为还包括动态方法的引用。所以所有动态方法在同一个实例对象中共享同一个同步锁。楼主可以根据这些原理,再去回顾下synchronize互斥的情况。而匿名类的方法,则是在匿名“类”的空间里面。所以synchronize加锁的时候必须额外指定域。
    此外,15楼、18楼的程序,synchronize都是加在方法上面。这个范围还稍微有点大。再看下面的例子。这个例子就请楼主自己来分析synchronized的域。public class Test {
    private Fun ff = new Fun(); public Test() {
    new Thread(new Runnable() {
    public void run() {
    Test.this.threadWait();
    System.out.println("恢复线程");
    Test.this.ff.a();
    }
    }).start(); new Thread(new Runnable() {
    public void run() {
    Test.this.ff.b();
    Test.this.threadNotify();
    }
    }).start();
    } /**
     * @param args
     */
    public static void main(String[] args) {
    Test t = new Test();
    } public void threadWait() {
    synchronized (this) {
    try {
    this.wait();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    } public void threadNotify() {
    synchronized (this) {
    this.notify();
    }
    }
    }class Fun {
    public void a() {
    for (int i = 0; i < 100; i++) {
    System.out.println("this  is A:" + i);
    }
    } public void b() {
    for (int i = 0; i < 100; i++) {
    System.out.println("this  is B:" + i);
    }
    }
    }