首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > JAVA > J2SE开发 >

多线程有关问题 wait() notifyAll(); 出错

2012-12-23 
多线程问题 wait()notifyAll() 出错package testpublic class test {/** * @param args */public static

多线程问题 wait() notifyAll(); 出错


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)
[最优解释]
引用:
再看下这个程序,就可以更了解wait()、notify()的使用方式了。
Java code?12345678910111213141516171819202122232425262728293……

此外,我记得我还回复过楼主在另一个帖子中的问题。

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

没有错误呀!!!这是截图
[其他解释]
wait 和notify 之类的必须都放在进程同步块或者同步方法中。用synchronized 关键字。
[其他解释]
说错了,是线程
引用:
wait 和notify 之类的必须都放在进程同步块或者同步方法中。用synchronized 关键字。

[其他解释]
又看到了熟悉的wait()、notify()……线程啥的我最在行了

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


借用一下。。。

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


[其他解释]
哦……貌似是我有些搞错了……
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方法同领域的线程。



[其他解释]
引用:
 {     /**     * @param args     */    pu……


再看下这个程序,就可以更了解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()运行完毕");
}
}

[其他解释]
引用:
wait 和notify 之类的必须都放在进程同步块或者同步方法中。用synchronized 关键字。


其实我对 synchronized  的理解是互斥 ,就是A线程正在使用synchronized保护的线程,B线程只有等待A线程执行完了才可以进去,,,同步这个怎么理解?
[其他解释]
引用:
引用: {     /**     * @param args     */    pu……

再看下这个程序,就可以更了解wait()、notify()的使用方式了。
Java code?1234567891011121314151617181920212223242526272829303132333435363738394041……


请问为什么非要用synchronized 括起来才可以。。。?

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

请问为什么非要用synchronized 括起来才可以。。。?

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


请看我在7楼的回复。这涉及到synchronized的范围问题。
[其他解释]
引用:
请问为什么非要用synchronized 括起来才可以。。。?

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


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中添加多个实例,也就能添加多个同步域了。
[其他解释]
引用:
引用:请问为什么非要用synchronized 括起来才可以。。。?

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

synchronized 翻译为“同步”,但其实其作用有两个:
一是生成一个同步锁;二是……


帮我看看  下面这个  那里出错了,

按你的意思,我自己定义了一个实例。。。  还是出错


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

}


[其他解释]
引用:
帮我看看  下面这个  那里出错了,

按你的意思,我自己定义了一个实例。。。  还是出错


啊哈,我就是写了个类似的程序。请看:

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


[其他解释]
然后我把那个很罗嗦的程序改了下。
这里我把那个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());
}
}
}


[其他解释]
引用:
帮我看看  下面这个  那里出错了


楼主要把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();
}
}

[其他解释]
引用:
public void run() {
                try {


                    object.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
 
                ff.a();
 
            }
        }).start();


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

楼主要把synchronize放到Fun里面是吧?程序如下:
Java code?1234567891011121314151617181920212223242526272829303132333435363738394041public class Test {    /**    ……



你楼上的2个例子  第二个例子

如果把    public synchronized static void b() 改成   public synchronized  void b()
也必须用这个:Fun.class.notifyAll();  吗?


[其他解释]
LZ学到 线程了,类名还写不规范哦,我先去上课,回来看看代码。
[其他解释]
手机只在开机页面停住了怎么办
[其他解释]
引用:
你楼上的2个例子  第二个例子

如果把    public synchronized static void b() 改成   public synchronized  void b()
也必须用这个:Fun.class.notifyAll();  吗?


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


[其他解释]
我对线程理解不深,需要加深理解

热点排行