最近在学习Java多线程中,遇到了一个问题,代码如下:
class Obj {
//交替打印张三与李四的文字
public static int flag = 0;
public String name;
Obj(String name) {
this.name = name;
}
public synchronized void print() {
System.out.print(name + "::");
System.out.println("打印一段文字");
}
}
public class ExceptionInWait {
public static void main(String[] args) {
Obj o1 = new Obj("张三");
Obj o2 = new Obj("李四");
Thread t1 = new Thread() {
@Override
public void run() {
int flag = 1;
while (true) {
if (flag == Obj.flag) {
o1.print();
<span style="white-space:pre"> </span>Obj.flag = 0;
<span style="white-space:pre"> </span>o1.notifyAll(); // 错误1
<span style="white-space:pre"> </span> } else {
<span style="white-space:pre"> </span>try {
<span style="white-space:pre"> </span> o1.wait(); // 错误2
<span style="white-space:pre"> </span>} catch (InterruptedException e) {
<span style="white-space:pre"> </span> e.printStackTrace();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span> }
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span> }
<span style="white-space:pre"> </span>};
<span style="white-space:pre"> </span>Thread t2 = new Thread() {
<span style="white-space:pre"> </span> @Override
<span style="white-space:pre"> </span> public void run() {
<span style="white-space:pre"> </span>int flag = 0;
<span style="white-space:pre"> </span>while (true) {
<span style="white-space:pre"> </span> if (flag == Obj.flag) {
<span style="white-space:pre"> </span>o2.print();
<span style="white-space:pre"> </span>Obj.flag = 1;
<span style="white-space:pre"> </span>o1.notifyAll(); // 错误3
<span style="white-space:pre"> </span> } else {
<span style="white-space:pre"> </span>try {
<span style="white-space:pre"> </span> o2.wait();// 错误4
<span style="white-space:pre"> </span>} catch (InterruptedException e) {
<span style="white-space:pre"> </span> e.printStackTrace();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span> }
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span> }
<span style="white-space:pre"> </span>};
<span style="white-space:pre"> </span>t1.start();
<span style="white-space:pre"> </span>t2.start();
}
}
出现错误的地方我标明了,作为一个java新手,之前很不理解为什么会导致这样的错误。在仔细阅读了Oracle官方教程中的Concurrency这一章之后,才有所领悟
When a thread invokes d.wait, it must own the intrinsic lock for d
然而在对象中,因为print方法持有synchronized关键字,于是执行完后,已经都把各自的锁给释放掉了
使用wait和notify方法之前,一定要确认当前线程持有的锁对象,如果锁对象不存在,就会发生IllegalMonitorStateException;如果锁对象存在,但是没有像预期一样设置好,程序的运行结果会与预期不同
原文链接:http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
第一次用编辑器,懒得去纠正错误代码的格式了,下面贴上正确的代码
public class ExceptionInWait {
public static void main(String[] args) {
Obj o1 = new Obj("张三", 0);
Obj o2 = new Obj("李四", 1);
final Object lock = new Object();
o1.setLock(lock);
o2.setLock(lock);
Thread t1 = new Thread() {
@Override
public void run() {
while (true) {
o1.print();
}
}
};
t1.setName("张三的线程");
Thread t2 = new Thread() {
@Override
public void run() {
while (true) {
o2.print();
}
}
};
t2.setName("李四的线程");
t1.start();
t2.start();
}
}
class Obj {
public static int flag = 0;
public int targetFlag;
public String name;
private Object lock;
public void setLock(Object lock) {
this.lock = lock;
}
Obj(String name, int targetFlag) {
this.name = name;
this.targetFlag = targetFlag;
}
public void print() {
synchronized (lock) {
if (targetFlag == flag) {
System.out.print(name + "::");
System.out.println("打印一段文字");
//Alter the condition
if(Obj.flag==1){
Obj.flag=0;
}else{
Obj.flag=1;
}
lock.notifyAll();//线程通讯一定要使用正确的锁对象;wait和notify方法的前提是该线程持有锁对象!
}else{
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}