wait()与notify()获取对象监视器(锁)的三种方式

在某个线程方法中对wait()和notify()的调用必须指定一个Object对象,而且该线程必须拥有该Object对象的monitor。而获取对象monitor最简单的办法就是,在对象上使用synchronized关键字。当调用wait()方法以后,该线程会释放掉对象锁,并进入sleep状态。而在其它线程调用notify()方法时,必须使用同一个Object对象,notify()方法调用成功后,所在这个对象上的相应的等侍线程将被唤醒。
对于被一个对象锁定的多个方法,在调用notify()方法时将会任选其中一个进行唤醒,而notifyAll()则是将其所有等待线程唤醒。
package  net.mindview.util;

import  javax.swing.JFrame;

public  class  WaitAndNotify {
        public  static  void  main(String[] args) {
            System.  out .println( "Hello World!"  );
            WaitAndNotifyJFrame frame =  new  WaitAndNotifyJFrame();
            frame.setDefaultCloseOperation(JFrame.  EXIT_ON_CLOSE );
              // frame.show();
            frame.setVisible(  true );
      }
}

@SuppressWarnings ( "serial"  )
class  WaitAndNotifyJFrame  extends  JFrame {

        private  WaitAndNotifyThread  t  ;

        public  WaitAndNotifyJFrame() {
            setSize(300, 100);
            setLocation(250, 250);
            JPanel panel =  new  JPanel();
            JButton start =  new  JButton( new  AbstractAction( "Start" ) {
                    public  void  actionPerformed(ActionEvent event) {
                          if  ( t  ==  null ) {
                                t  =  new  WaitAndNotifyThread(WaitAndNotifyJFrame. this );
                                t .start();
                        }  else  if  ( t  . isWait  ) {
                                t .  isWait  =  false  ;
                                t .n();
                                // t.notify();
                        }
                  }
            });
            panel.add(start);
            JButton pause =  new  JButton( new  AbstractAction( "Pause" ) {
                    public  void  actionPerformed(ActionEvent e) {
                          if  ( t  !=  null ) {
                                t .  isWait  =  true  ;
                        }
                  }
            });
            panel.add(pause);
            JButton end =  new  JButton( new  AbstractAction( "End" ) {
                    public  void  actionPerformed(ActionEvent e) {
                          if  ( t  !=  null ) {
                                t .interrupt();
                                t  =  null ;
                        }
                  }
            });
            panel.add(end);
            getContentPane().add(panel);
      }

}

@SuppressWarnings ( "unused"  )
class  WaitAndNotifyThread  extends  Thread {

        public  boolean  isWait  ;
        private  WaitAndNotifyJFrame  control  ;
        private  int  count  ;

        public  WaitAndNotifyThread(WaitAndNotifyJFrame f) {
              control  = f;
              isWait  =  false  ;
              count  = 0;
      }

        public  void  run() {
              try  {
                    while  ( true  ) {
                          synchronized  ( this  ) {
                              System.  out .println( "Count:"  +  count ++);
                               sleep(100);
                                if  ( isWait  )
                                    wait();
                        }
                  }
            }  catch  (Exception e) {
            }
      }
       
      public   void  n() {
              synchronized  ( this  ) {
                  notify();
            }
      }

}
如上面例子方框中的代码,若去掉同步代码块,执行就会抛出 java.lang.IllegalMonitorStateException 异常。
查看JDK,我们可以看到,出现此异常的原因是 当前线程不是此对象监视器的所有者

此方法只应由作为此对象监视器的所有者的线程来调用,通过以下三种方法之一,可以使线程成为此对象监视器的所有者:
1,通过执行此对象的同步实例方法,如:
             public  synchronized  void  n() {
          notify();
      }
2,通过执行在此对象上进行同步的synchronized语句的正文,如:
       public  void  n() {
           synchronized  ( this  ) {
              notify();
          }
      }
3,对于Class类型的对象,可以通过执行该类的同步静态方法。
在调用静态方法时,我们并不一定创建一个实例对象。因此,就不能使用this来同步静态方法,所以必须使用Class对象来同步静态方法,由于notify()方法不是静态方法,所以我们无法将n()方法设置成静态方法,所以采用另外一个例子加以说明:
public  class  SynchronizedStatic  implements  Runnable {

        private  static  boolean  flag  =  true ;

//类对象同步方法一:       
      // 注意static修饰的同步方法,监视器:SynchronizedStatic.class
        private  static  synchronized  void  testSyncMethod() {
              for  ( int  i = 0; i < 100; i++) {
                    try  {
                        Thread. sleep(100);
                  }  catch  (InterruptedException e) {
                        e.printStackTrace();
                  }
                  System.  out .println( "testSyncMethod:"  + i);
            }
      }


//类对象同步方法二:       
             private  void  testSyncBlock() {
             // 显示使用获取class做为监视器.它与static  synchronized  method隐式获取class监视器一样.
              synchronized  (SynchronizedStatic.  class ) {
                    for  ( int  i = 0; i < 100; i++) {
                          try  {
                              Thread. sleep(100);
                        }  catch  (InterruptedException e) {
                              e.printStackTrace();
                        }
                        System.  out .println( "testSyncBlock:"  + i);
                  }
            }
      }


        public  void  run() {
              // flag是static的变量.所以,不同的线程会执行不同的方法,只有这样才能看到不同的锁定效果.
              if  ( flag  ) {
                    flag  =  false  ;
                   testSyncMethod();
            }  else  {
                    flag  =  true  ;
                  testSyncBlock();
            }
      }

        public  static  void  main(String[] args) {
            ExecutorService exec = Executors. newFixedThreadPool(2);
            SynchronizedStatic rt =  new  SynchronizedStatic();
            SynchronizedStatic rt1 =  new  SynchronizedStatic();
            exec.execute(rt);
            exec.execute(rt1);
            exec.shutdown();
      }
}
以上代码的运行结果是,让两个同步方法同时打印从0到99这100个数,其中方法一是一个静态同步方法,它的作用域为类;方法二显示的声明了代码块的作用域是类。这两个方法的异曲同工的。由于方法一和方法二的作用域同为类,所以它们两个方法间是互斥的,也就是说,当一个线程调用了这两个方法中的一个,剩余没有调用的方法也会对其它线程形成阻塞。因此,程序的运行结果会是:
testSyncMethod:0
testSyncMethod:1
... ...
testSyncMethod:99
testSyncBlock:0
... ...
testSyncBlock:99

但是,如果我们将方法二中的 SynchronizedStatic.  class 替换成this的话,由于作用域的没,这两个方法就不会形成互斥,程序的输出结果也会交替进行,如下所示:
testSyncBlock:0
testSyncMethod:0
testSyncBlock:1
testSyncMethod:1
... ...
testSyncMethod:99
testSyncBlock:99

由上一篇博文,我们已经说明,锁(lock)的作用域有两种,一种是类的对象,另一种的类本身。在以上代码中给出了两种使锁的作用范围为类的方法,这样就可以使同一个类的不同对象之间也能完成同步。

总结以上,需要注意的有以下几点:
1,wait()、notify()、notifyAll()都需要在拥有对象监视器的前提下执行,否则就会抛出 java.lang.IllegalMonitorStateException 异常。
2,多个线程可以同时在一个对象上等待。
3, notify()是随机唤醒一个在对象上等待的线程,若没有等待的线程,则什么也不做。
4, notify()唤醒的线程,并不是在 notify()执行以后就立即唤醒,而是在 notify()线程释放了对象监视器之后才真正执行被唤醒的线程。
5,Object的这些方法与Thread的sleep、interrupt方法相差还是很远的,不要混为一谈。





参考资料:
jdk中文文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值