黑马程序员技术交流社区

标题: 线程问题(已解决) [打印本页]

作者: 杨锦    时间: 2012-8-5 18:04
标题: 线程问题(已解决)
本帖最后由 杨锦 于 2012-8-5 19:21 编辑

class Exercise {
public static void main(String args[]) {
  final Print p=new Print();
  new Thread() {
   public void run() {
   
    for(;;)
     p.printer1();
   
   }
  }.start();
  
  new Thread() {
   public void run() {
   
    for(;;)
     p.printer2();
   
   }
  }.start();
}
}
class Print {
private int flag=1;
private Object obj = new Object();
public  void  printer1(){
  synchronized(obj){                    //用 synchronized代码块就会报异常
  if(flag==2)
  try{
          wait();
   }catch(Exception e){
   e.printStackTrace();
   }  
  System.out.println("黑");
  System.out.println("马");
  System.out.println("最");
  System.out.println("牛");

          notify();
  flag=2;
}
}

public  synchronized void printer2(){              //为什么用 synchronized方法就不报异常呢
   if(flag==1)
   try{
          wait();
  }catch(Exception e){
   e.printStackTrace();
   }
  System.out.println("I");
  System.out.println("L");
  System.out.println("O");
  System.out.println("V");
  System.out.println("E");
  System.out.println("Y");
  System.out.println("O");
  System.out.println("U");
  
         notify();
  flag=1;
  
}
}

     // 我已经try.....catch了啊,为什么还一直报异常!!!!

QQ截图20120805180359.jpg (46.97 KB, 下载次数: 47)

QQ截图20120805180359.jpg

作者: 左建飞    时间: 2012-8-5 19:07
wait()、notify()、notifyAll()这三个方法是必须写在同步块或同步函数中的。
因为这写方法是对持有锁的线程操作的。
只有同一个锁上的被等待线程,可以被同一个锁上的notify()唤醒,不可以被不同锁上的线程进行唤醒。
所以这些方法的前面都必须标明调用这个方法的锁。
用同步函数时,默认的锁是this.,也就是你程序中的p了。这时不用标明wait()和notify()方法前的锁也是可以的。
而同步代码块中是用的锁是obj,这是你就必须表明了。
但是这时,你的等待唤醒机制所是用的锁是不同的锁了。我认为你的程序虽然不会报错了,但是却达不到预期的效果了。
所以你可以在同步方法中都标明锁obj,也就是所有的wait()、notify()方法都写成obj.wait();obj.notify();
第二种解决方式当然就是全部写成同步方法了。这样最方便也最保险。
作者: 王晓龙    时间: 2012-8-5 19:12
class Exercise {
public static void main(String args[]) {
  final Print p=new Print();
  new Thread() {
   public void run() {
   
    for(;;)
     p.printer1();
   
   }
  }.start();
  
  new Thread() {
   public void run() {
   
    for(;;)
     p.printer2();
   
   }
  }.start();
}
}
class Print {
private int flag=1;
//private Object obj = new Object();
public  void  printer1(){
  synchronized(this){                    //用 synchronized代码块就会报异常
  // 你这里的对象和 下面的不同 应该用this 调用当前的对象 或者下面也用obj对象作为锁 你这个同步代码块和下面的同步方法用的不是相同的key 所以根本就不能同步
  if(flag==2)
  try{
          wait();
   }catch(Exception e){
   e.printStackTrace();
   }  
  System.out.println("黑");
  System.out.println("马");
  System.out.println("最");
  System.out.println("牛");

          notify();
  flag=2;
}
}

public  synchronized void printer2(){              //为什么用 synchronized方法就不报异常呢
   if(flag==1)
   try{                                                                                        //        你把同步代码块的key 用this 他们的key相同啦 就不报异常啦
          wait();
  }catch(Exception e){
   e.printStackTrace();
   }
  System.out.println("I");
  System.out.println("L");
  System.out.println("O");
  System.out.println("V");
  System.out.println("E");
  System.out.println("Y");
  System.out.println("O");
  System.out.println("U");
  
         notify();
  flag=1;
  
}
}

作者: 王晓龙    时间: 2012-8-5 19:13
补充下 上面的key指的是所调用对象
作者: hello world    时间: 2012-8-5 19:19
汗。去文档查查你的异常,非法的监视器状态异常,原因就是同步代码块使用的锁是obj,而你的wait(),notify()调用时用的是this,而他们的调用需要你用你的所对象才可以。而同步方法用的锁对象就是this,所以就不会报告异常了。

作者: 张雪磊    时间: 2012-8-5 19:24
本帖最后由 张雪磊 于 2012-8-5 19:30 编辑

这个实际上是线程通信的问题,你的这个代码是在printer1线程和printer2之间进行相互唤醒的通信。这时候必须保证两个同步代码块使用同一把锁,而楼主的两个代码块一个使用的是obj锁,一个用的是this锁,也就是说这两个线程实际上并未同步,还在各自执行自己的run方法内容。
另外就需要了解notify,natifyAll,wait这三个方法了
这三个方法使用时,当前线程必须是当前对象锁资源的持有者,否则就会报IllegalMonitorStateException这个错误。由于两个代码块持有的锁资源不同,为实现同步,就报错了。
如果了解IllegalMonitorStateException 这个错误在上面时候下出现就容易解决了,
这个异常会在三种情况下抛出:1>当前线程不含有当前对象的锁资源的时候,调用wait()方法;2>当前线程不含有当前对象的锁资源的时候,调用notify()方法。3>当前线程不含有当前对象的锁资源的时候,调用notifyAll()方法。如果楼主不想用同步函数,而要用同步代码块的话,用下面这种写法就不会报错了。

class Exercise {
public static void main(String args[]) {
  final Print p=new Print();
  new Thread() {
   public void run() {

    for(;;)
     p.printer1();

   }
  }.start();

  new Thread() {
   public void run() {

    for(;;)
     p.printer2();

   }
  }.start();
}
}
class Print {
private int flag=1;
private Object obj = new Object();
public void  printer1(){
  synchronized(obj){                   //用 synchronized代码块就会报异常
  if(flag==2)
  try{
          obj.wait();//注意蓝色的部分必须加上obj.否则还会报错,这时候锁变成obj而不是this了,如果你省略那默认还是this,但实际上用的是obj锁而不是this,所以必须写上obj.
   }catch(Exception e){
   e.printStackTrace();
   }  
  System.out.println("黑");
  System.out.println("马");
  System.out.println("最");
  System.out.println("牛");

         obj.notify();
  flag=2;

}
}

public  void printer2(){              //为什么用 synchronized方法就不报异常呢
    synchronized(obj){
    if(flag==1)
   try{
         obj. wait();
  }catch(Exception e){
   e.printStackTrace();
   }
  System.out.println("I");
  System.out.println("L");
  System.out.println("O");
  System.out.println("V");
  System.out.println("E");
  System.out.println("Y");
  System.out.println("O");
  System.out.println("U");

         obj.notify();
  flag=1;
    }
}
}






欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2