zoukankan      html  css  js  c++  java
  • 线程中的wait() 与 锁的关系

    我们先看一段代码:

    /** 
    * 计算输出其他线程锁计算的数据
    * 
    */ 
    public class ThreadA {
        public static void main(String[] args) throws InterruptedException{
            ThreadB b = new ThreadB();
            //启动计算线程
            b.start(); 
            //线程A拥有b对象上的锁。线程为了调用wait()或notify()方法,该线程必须是那个对象锁的拥有者
            synchronized (b) {
                System.out.println("等待对象b完成计算。。。");
                //当前线程A等待
                b.wait();
                System.out.println("b对象计算的总和是:" + b.total);
            } 
        } 
    }
    
     
    
    /** 
    * 计算1+2+3 ... +100的和
    * 
    */ 
    class ThreadB extends Thread {
        int total; 
    
        public void run() {
            synchronized (this) {
                for (int i = 0; i < 101; i++) {
                    total += i; 
                } 
                //(完成计算了)唤醒在此对象监视器上等待的单个线程,在本例中线程A被唤醒
                notify(); 
                System.out.println("计算完成");
            } 
        } 
    }

    执行结果:

    等待对象b完成计算。。。
    计算完成
    b对象计算的总和是:5050

    如果我们将b.wait()去掉呢?结果如下:

    等待对象b完成计算。。。
    b对象计算的总和是:0
    计算完成

    上述的结果表明,当去掉b.wait()时,新启动的线程ThreadB与主线程ThreadA是各自执行的,没有线程等待的现象。

    我们想要的效果是,当线程ThreadB完成计算之后,再去取计算后的结果。所以使用了b.wait()来让主线程等待。

    那为什么是使用b.wait(),而不是Thread.currentThread.wait(),或者其他的呢?

    如果我们将b.wait()替换成Thread.currentThread.wait(),将会得到如下的结果:

    Exception in thread "main" java.lang.IllegalMonitorStateException
    at java.lang.Object.wait(Native Method)
    at java.lang.Object.wait(Object.java:485)
    at pa.com.thread.ThreadA.main(ThreadA.java:18)
    等待对象b完成计算。。。
    计算完成

    替换的代码Thread.currentThread.wait()好像理所当然应该如我们预期的正确啊,让当前线程处于等待状态,让其他线程先执行。

    我们忽略了一个很重要的问题:线程与锁是分不开的,线程的同步、等待、唤醒都与对象锁是密不可分的。

    线程ThreadA持有对象b的锁,我们要使用这把锁去让线程释放锁,从而让其他的线程能抢到这把锁。

    从我们的程序来分析就是:线程ThreadA首先持有锁对象b,然后调用b.wait()将对象锁释放,线程ThreadB争抢到对象锁b,从而执行run()方法中的计算,计算完了之后使用notify()唤醒主线程ThreadA,ThreadA得以继续执行,从而得到了我们预期的效果。

    (之所以ThreadB的对象锁也是b,是因为synchronized(this)中的this指向的就是ThreadB的实例b)

    Thread.currentThread.wait()调用的是当前线程对象(即主线程ThreadA)的wait()方法,当前线程对象ThreadA是没有被加锁的,它只是获取了对象锁b。我基本没有看到过这样的调用,一般使用的是锁对象的wait(),本例中为b.wait()

    顺带讲一下wait()与sleep()的区别。

    如果我们将b.wait()换成Thread.sleep(1000),则会出现如下的结果:

    等待对象b完成计算。。。
    b对象计算的总和是:0
    计算完成

    从执行结果可以看出,Thread.sleep(1000)只是让主线程ThreadA睡眠了1秒钟,而并没有释放对象锁,所以在主线程ThreadA睡眠的过程中,ThreadB拿不到对象锁,从而不能执行。

    所以我们也就得出了如下的结论:

    wait()方法是让线程释放对象锁,让其他线程拿到锁之后去优先执行,当其他全程唤醒wait()中的线程 或者 拿到对象锁的线程都执行完释放了对象锁之后,wait()中的线程才会再次拿到对象锁从而执行。

    sleep()方法是让线程睡眠,此时并没有释放对象锁,其他想要拿到睡眠线程的对象锁的线程也就拿不到相应的对象锁,从而不能抢在它前面执行。

    补:

    wait、notify和notifyAll方法是Object类的final native方法。所以这些方法不能被子类重写,Object类是所有类的超类,因此在程序中有以下三种形式调用wait等方法。

    wait();//方式1:
    this.wait();//方式2:
    super.wait();//方式3

    void wait()

    导致线程进入等待状态,直到它被其他线程通过notify()或者notifyAll唤醒。该方法只能在同步方法中调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。

     

  • 相关阅读:
    ADexplorer
    Ldap登陆AD(Active Directory)进行认证的Java示例
    通过LDAP验证Active Directory服务
    APACHE + LDAP 的权限认证配置方法
    How to authenticate a user by uid and password?
    js汉字与拼音互转终极方案,附简单的JS拼音输入法【转】
    给MySQL增加mysql-udf-http和mysql-udf-json自定义函数,让MySQL有调用http接口和查询直接回JSON的能力
    CentOS6.7安装RabbitMQ3.6.5
    CentOS利用inotify+rsync实现文件同步
    CentOS两台服务器利用scp拷贝文件
  • 原文地址:https://www.cnblogs.com/kevin-yuan/p/4112434.html
Copyright © 2011-2022 走看看