多线程问题 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);
}
}
[其他解释]
哦……貌似是我有些搞错了……
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方法同领域的线程。
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()运行完毕");
}
}
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);
}
}
}
}
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());
}
}
}
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 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);
}
}
}