今天同学问我一个问题,为什么他执行的代码不会结束,代码如下:
public class TestAccount {
public static void main(String[] args) {
// TODO Auto-generated method stub
Account acc = new Account();
new Thread(acc).start();
new Thread(acc).start();
}
}
class Account implements Runnable {
int balance = 0;
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 3; i++) {
synchronized (this) {
notify();
balance += 1000;
System.out.println(Thread.currentThread().getName() + "存入1000元,账户余额为:" + balance);
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
其运行结果如下:
Thread-0存入1000元,账户余额为:1000
Thread-1存入1000元,账户余额为:2000
Thread-0存入1000元,账户余额为:3000
Thread-1存入1000元,账户余额为:4000
Thread-0存入1000元,账户余额为:5000
Thread-1存入1000元,账户余额为:6000
从代码里可以看出,一个有两个线程,共享一个对象,也就是多线程编程的问题,多线程操作一个共享变量,使用了同步代码块来实现的并发编程。其实,代码没有结束的原因很简单,就是Thread-1在最后一次执行后,调用了wait方法,还在那等待,所以程序始终没有结束。
注意点
这里要注意的就是,notify()调用后,并不是马上就释放对象锁的,而是在相应的同步块或同步方法中执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。所以说,在同步代码块或者同步方法上,notify放在哪个位置其实没有什么影响。
流程分析
1.
开始,线程0和线程1都执行了,但是因为线程0获得了锁,所以线程1被阻塞, 线程0 执行完 balance += 1000;后打印1000,调用wait,释放锁同时也释放cpu资源,最后调用notify,此时线程1收到唤醒通知。
此时线程0的i=0
2.
线程1收到通知后,然后获得锁,执行完balance += 1000;后打印2000,然后调用wait,释放锁同时也释放cpu资源,最后调用notify,此时线程0收到唤醒通知。
此时线程1的i=0
3.
线程0收到通知后,然后获得锁,执行完balance += 1000;后打印3000,然后调用wait,释放锁同时也释放cpu资源,最后调用notify,此时线程1收到唤醒通知。
此时线程0的i=1
4.
线程1收到通知后,然后获得锁,执行完balance += 1000;后打印4000,然后调用wait,释放锁同时也释放cpu资源,最后调用notify,此时线程0收到唤醒通知。
此时线程1的i=1
5.
线程0收到通知后,然后获得锁,执行完balance += 1000;后打印5000,然后调用wait,释放锁同时也释放cpu资源,最后调用notify,此时线程1收到唤醒通知。
此时线程0的i=2
6.
线程1收到通知后,然后获得锁,执行完balance += 1000;后打印6000,然后调用wait,释放锁同时也释放cpu资源,最后调用notify,此时线程0收到唤醒通知。
此时线程1的i=2
7.
线程0收到通知后,被唤醒,然后执行i++之后i=3,然后执行for循环后,发现不符合循环条件,最后跳出for循环,线程0执行完毕。
然而,因为线程0执行完毕,线程1永远也收不到通知,所以线程1一直在等待,所以这就是程序永远不会终止的原因所在。