• java之Thread.sleep(long)与object.wait()/object.wait(long)的区别及相关概念梳理(good)


    一、Thread.sleep(long)与object.wait()/object.wait(long)的区别
    sleep(long)与wait()/wait(long)行为上有些类似,主要区别如下:
    1.Thread.sleep(long)是属于Thread类的静态方法。其基本语义是使当前运行的线程暂停一段时间。实现细节是把当前线程放入就绪线程队列中,直到睡眠时间到期才可被调度为执行线程(在时间到期前无法被调度为执行线程)。此方法可以在sychronized代码块中,调用此方法不释放对象锁;也可以不在sychronized代码块中。此方法的作用线程一定是当前运行的线程(如果代码在一个线程类中,不一定是代码所在的线程实例),即使是在线程对象上调用sleep(long)或调用Thread.currentThread().sleep(long)。


    2.object.wait()是属于类实例(Object及其子类实列,也包括Class类实例)的方法。
    其基本语义是使当前线程等待,直到被通知,默认是this.wait()。实现细节是把当前线程放入阻塞线程队列中,并把当前线程注册为指定对象的监听器,并锁释放指定对象的锁;当被notify/notifyAll通知时,重新争取指定对象的锁,并把当前线程从指定对象的监听器中移除,把当前线程从阻塞队列放入就绪队列,等待被调度。
    此方法必须在sychronized代码块中,且锁定的对象要与等待通知来源的对象一致。而wait(long)方法阻塞时放入的是就绪队列,等待时间到期或被通知就可被调度,其他与wait()方法相同。
    如果当前线程不是对象所得持有者,该方法抛出一个java.lang.IllegalMonitorStateException 异常”
    http://blog.csdn.net/intlgj/article/details/6245226

    注:从上可基本认识到线程的执行、就绪、阻塞三种状态的切换,以及线程的调度(操作系统调度线程)和就绪线程队列、阻塞线程队列(实际实现可能更复杂,比如优先级,调度策略等)。可认识到object(包括Class类的实例)的wait/notify/notifyAll的对象监听和通知事件模式,以及对象上的锁、锁队列(实际实现可能更复杂,比如优先级,锁争用等)、阻塞线程监听队列(notify时只通知一个监听器,具体调度未知,另有自动唤醒,条件唤醒)。

    http://blog.csdn.net/qingmingcom/article/details/6590967

        wait()方法与notify()必须要与synchronized(resource)一起使用。也就是wait与notify针对已经获取了resource锁的线程进行操作
    从语法角度来说就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内。
    从功能上来说wait()线程在获取对象锁后,主动释放CPU控制权,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。
    相应的notify()就是对对象锁的释放操作
    【因此,我们可以发现,
    wait和notify方法均可释放对象的锁,但wait同时释放CPU控制权,即它后面的代码停止执行,线程进入阻塞状态,而notify方法不立刻释放CPU控制权,而是在相应的synchronized(){}语句块执行结束,再自动释放锁
    释放锁后,JVM会在等待resoure的线程中选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操作。
    Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制
    而在同步块中的
    Thread.sleep()方法并不释放锁,仅释放CPU控制权。

      1)wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。

      2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁,或者叫管程)

      3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;

      4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;

         在Java中,是没有类似于PV操作、进程互斥等相关的方法的。JAVA的进程同步是通过synchronized()来实现的,需要说明的是,Java的synchronized()方法类似于操作系统概念中的互斥内存块,在Java中的Object类对象中,都是带有一个内存锁的,在有线程获取该内存锁后,其它线程无法访问该内存,从而实现Java中简单的同步、互斥操作。明白这个原理,就能理解为什么synchronized(this)与synchronized(static XXX)的区别了,synchronized就是针对内存区块申请内存锁,this关键字代表类的一个对象,所以其内存锁是针对相同对象的互斥操作,而static成员属于类专有,其内存空间为该类所有成员共有,这就导致synchronized()对static成员加锁,相当于对类加锁,也就是在该类的所有成员间实现互斥,在同一时间只有一个线程可访问该类的实例。如果需要在线程间相互唤醒就需要借助Object类的wait()方法及nofity()方法。

    http://blog.csdn.net/lingzhm/article/details/44940823

    什么是monitor?

    在HotSpot虚拟机中,monitor采用ObjectMonitor实现。

    每个线程都有两个ObjectMonitor对象列表,分别为free和used列表,如果当前free列表为空,线程将向全局global list请求分配ObjectMonitor。

    ObjectMonitor对象中有两个队列:
    _WaitSet 和 _EntryList,用来保存ObjectWaiter对象列表;
    _owner指向获得ObjectMonitor对象的线程。

    _WaitSet :处于wait状态的线程,会被加入到wait set;
    _EntryList:处于等待锁block状态的线程,会被加入到entry set;

    ObjectWaiter

    ObjectWaiter对象是双向链表结构,保存了_thread(当前线程)以及当前的状态TState等数据, 每个等待锁的线程都会被封装成ObjectWaiter对象。

    http://www.jianshu.com/p/f4454164c017

    我刚开始深入研究多线程,一直认为Object.wait()/Condition.await()让当前线程阻塞的同时,也会释放当前线程对该condition对象的锁。在之前的一些测试代码中也显示wait后,线程上的锁被释放了。
    查看Object.wait()API 描述如下:

    Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0). 
        The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method.
     The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

    其中“the thread releases ownership of this monitor” 说道当前线程会释放这个对象监控器的所有权。

    问题一:这里的monitor怎么理解?监视器(monitor)和锁是什么关系? 这个monitor就是一个术语,或者是一个特殊的类(只包含私有域),但是在java中并没有严格遵守这个概念。个人认为可以简单的理解为锁。

    同样的,Condition.await()方法中也有类似描述。
    The lock associated with this Condition is atomically released and the current thread becomes disabled for thread scheduling purposes...
    也说道,调用await()有,这个条件对象关联的锁被“原子级地”释放。。。

    问题二:这个原子级的释放是什么意思? “原子级”其实就是为了保证一个操作的完整性,原子级的释放保证了一个原子操作不会因为线程的突然挂起或者说阻塞而破坏这次操作。

    这都能说明调用wait()或者await()后,当前线程会释放该条件对象关联的锁吧?


    两个线程用object1做 wait/notify, 是这样:
    thread1 得到object1 的 monitor, 调用 object1.wait()
    - 释放object1 的 monitor, thread1 wait;

    thread2 得到 object1 的 monitor, 调用 object1.notify()
    - 激活thread1, 释放object1 的 monitor;

    thread1 得到 object1 的 monitor, 从object1.wait()返回, thread1接着执行.
    关于monitor, 这个是多进程/线程同步的 一个术语, 见:
    Operating Systems Design and Implementation, Third Edition
    section 2.2

    A monitor is a collection of procedures, variables, and data structures that are all grouped together in a special kind of module or package. 
    Processes may call the procedures in a monitor whenever they want to, but they cannot directly access the monitor's internal data structures from procedures declared outside the monitor. 
    This rule, which is common in modern object-oriented languages such as Java, was relatively unusual for its time, although objects can be traced back to Simula 67.
    
    In all cases, before this method can return the current thread must
    re-acquire the lock associated with this condition. When the
    thread returns it is guaranteed to hold this lock.

    会释放,其他线程执行Condition.signal(),之前的线程会重新获得锁,继续执行,
    AbstractQueuedSynchronizer.java 第2040行,释放锁

    https://segmentfault.com/q/1010000002390706

  • 相关阅读:
    Setup Oracle Direct NFS Client
    Applying online patch on 11gr2
    几个关于oracle 11g ASM的问题
    推理实践丨如何使用MindStudio进行Pytorch模型离线推理
    使用高斯Redis实现二级索引
    如何在软件研发阶段落地安全实践
    4种Kafka网络中断和网络分区场景分析
    张平安:加快云上数字创新,共建产业智慧生态
    一文读懂数仓中的pg_stat
    复杂查询so easy ,GaussDB(for Cassandra)推Lucene引擎全新解决方案
  • 原文地址:https://www.cnblogs.com/softidea/p/4162724.html
Copyright © 2020-2023  润新知