之前看过张孝闲讲线程中的一个实例,让主线程运行10次,然后子线程再运行20次,这样循环50次。今天自己闲着写了一下,刚写出的代码报了个java.lang.IllegalMonitorStateException的错误,网上查了一下,发现了问题是锁的问题,于是自己总结了一下。
先说一点最重要的:syschronized同步的基础必须是多个线程拥有同一个锁(对象锁,变量锁),不能没有锁权限,也不能是拥有不同锁的权限。下面请看实例。
先贴第一版的错误代码:
public class ThreadNotity { /** * * @param args * * @deprecated 线程1执行10次,线程2执行20次,循环50次 */ public static void main(String[] args) { // TODO Auto-generated method stub final A a = new A(); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 50; i++) { a.printA(); } } }).start(); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 50; i++) { a.printB(); } } }).start(); } } class A { private boolean isArun = true; private static Object lock = new Object(); public synchronized void printA() { while (!isArun) { try { A.class.wait(); // lock.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (int i = 0; i < 10; i++) System.out.println(Thread.currentThread().getName() + " i=" + i); isArun = false; A.class.notifyAll(); // lock.notifyAll(); } public synchronized void printB() { while (isArun) { try { A.class.wait(); // lock.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (int i = 0; i < 20; i++) System.out.println(Thread.currentThread().getName() + " i=" + i); isArun = true; A.class.notifyAll(); lock.notifyAll(); } }
这个程序不管用A.class.wait()还是lock.wait()都会报java.lang.IllegalMonitorStateException错误,百度一下java.lang.IllegalMonitorStateException API中解释为:抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。可以理解为,正在运行的线程并没有获得指定的对象锁(上述程序中具体指
class A和Object lock).
于是根据synchronized一般的定义方法,又写了几种解决方案。
1.synchronized 修改方法,
先尝试不加对象,直接调用wait(),notity()方法,程序可以运行成功;接着尝试用了this,this.wait(),this.notity(),程序运行成功。然后就在想这个this到底在指什么。显然,并不是A.class
和 lock.我们知道this一般是指向调用这个方法的对象,在本程序中,调用方法的对象是a,于是就将a作为方法的参数,传递进去,并选择synchronized 的对象锁为a,调用a.wait(),a.notityAll(),程序可以运行成功;然后又将a和this混用,依然可以运行成功。显然此处当用synchronized
修饰方法时,synchronized 获得的对象锁是调用此方法的对象的对象锁,即a,这里也可用this代替。
class A { private boolean isArun = true; private static Object lock = new Object(); //<span style="font-family: monospace; font-size: 13px; line-height: 19.2000007629395px; white-space: pre;">synchronized 修改方法</span> public synchronized void printA(A a) { while (!isArun) { try { wait(); // this.wait(); // a.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (int i = 0; i < 10; i++) System.out.println(Thread.currentThread().getName() + " i=" + i); isArun = false; notifyAll(); // this.notifyAll(); // a.notifyAll(); } public synchronized void printB(A a) { while (isArun) { try { wait(); // this.wait(); // a.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (int i = 0; i < 20; i++) System.out.println(Thread.currentThread().getName() + " i=" + i); isArun = true; notifyAll(); // this.notifyAll(); // a.notifyAll(); } }
2. synchronized 去修饰代码片段
synchronized 修饰代码片段时,锁可以自己指定,可以为对象lock,也可以为变量,或者是类的字节码
A.class。但是必须相同。
class A { private boolean isArun = true; private static Object lock = new Object(); public void printA(A a) { synchronized (lock) { while (!isArun) { try { //A.class.wait(); lock.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (int i = 0; i < 10; i++) System.out.println(Thread.currentThread().getName() + " i=" + i); isArun = false; lock.notifyAll(); //A.class.notifyAll(); } } public void printB(A a) { synchronized (lock) { while (isArun) { try { //A.class.wait(); lock.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (int i = 0; i < 20; i++) System.out.println(Thread.currentThread().getName() + " i=" + i); isArun = true; lock.notifyAll(); //A.class.notifyAll(); } } }