Java-并发-Java对象监视器和Condition监视器简述

对象监视器

  对象监视器实际上就是与每个对象关联的Monitor对象,也叫做管程锁。实际上监视器也是一个数据结构,里面维护着一系列等待队列、同步队列等。这里不过多讨论对象监视器,详细讨论在会面的Synchronized部分中会有,本文主要针对Java初学者介绍相关的API方法,为后面的线程通信做准备,具体的应用将会在线程通信部分有讲到。
  当多个线程持有的“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象)同步代码块中的代码!可以是实现线程同步!

方法

  为了方便线程协作通信,JAVA为提供了wait()和notifyAll以及notify()实现挂起线程,并且唤醒另外一个等待的线程的三个监视器方法(通知与等待)。

/**
 * 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,能够使得当前线程进入等待队列,导致当前线程等待WAITING状态。该wait方法应由当前的持有对象监视器来调用。
 * 想要唤醒该等待线程,也应该由其他持有相同的对象监视器的线程调用notify方法。即要求其他线程和需要被唤醒的等待线程持有相同的对象监视器。或者其他线程调用该线程的interrupt()方法, 该线程抛出InterruptedExce ption 异常返回。
 * Wait的两个重载方法:
 */
public final void wait()

/**
 * 限时等待timeout milliseconds(毫秒)
 * @param timeout
 */
public final native void wait(long timeout)

/**
 * 如果一个线程调用共享对象的该方法挂起后, 没有在指定的timeout ms 时间内被其他线程调用该共享变量的notify() 或者notifyAll() 方法唤醒,那么该函数还是会因为超时而返回。
 * 如果将timeout 设置为0 则和wait 方法效果一样,因为在wait 方法内部就是调用了wait(O)。需要注意的是,如果在调用该函数时,传递了一个负的timeout 则会抛出IllegalArgumentException 异常。
 *
 * @param timeout
 * @param nanos
 */
public final void wait(long timeout, int nanos)


/**
 * 线程调用对象监视器的notify()方法,唤醒一条在该对象监视器上调用wait系列方法后被挂起的线程。如果有多条线程在同一等待队列中等待,只会选择唤醒其中一个线程,并且该选择是任意性的。
 */
public final void notify();

/**
 * 调用notifyAll方法就可以唤醒对应对象监视器的等待队列当中所有的等待的线程。只对在该方法被调用之前等待的线程有效。
 */
public final void notifyAll();

特点

  wait方法、notify方法、notifyAll方法,在使用的时候,必须要有自己的同步监视器(锁对象)。换句话:wait方法、notify方法、notifyAll方法,必须注册在某个同步监视器上(锁上)。(拥有自己的锁对象)否则抛出一个异常:IllegalMonitorStateException - 如果当前线程不是此对象监视器的所有者。
  因此创建了一个锁对象,这个锁对象上就具备一组监视器方法。Wait、notify、nofityAll方法就可以使用。
为什么上面的方法定义在Object类中?
  wait、notify、nofityAll 监视器方法,在使用的时候,必须要由自己的锁对象来调用。锁对象是任意对象,Java中只要是对象均有一个自己的监视器对象,任意对象都能调用的方法就应该抽出来,定义在Object 类当中,毕竟Object是所有类型的超类!

Condition 监视器

Object的监视器方法与Condition接口的对比

  任意一个Java对象,都拥有一与之关联的唯一的监视器对象,为此Java为每个对象提供了一组监视器方法(定义在java.lang.Object上),主要包括wait()、wait(long timeout)、notify()以及notifyAll()方法,这些方法与synchronized同步关键字配合,可以实现等待/通知模式。JDK1.5出现的Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等待/通知模式,但是这两者在使用方式以及功能特性上还是有差别的。Object的监视器方法与Condition接口的对比如下:
监视器
  Condition可以和任意的锁对象结合,监视器方法不会再绑定到某个锁对象上。使用Lock锁之后,相当于Lock 替代了synchronized方法和语句的使用,Condition替代了Object监视器方法的使用。
在Condition中,Condition对象当中封装了监视器方法,并用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现,这里注意,Condition是被绑定到Lock上的,要创建一个Lock的Condition必须用newCondition()方法。
  Condition的强大之处在于它可以为多个线程间建立不同的Condition,使用synchronized/wait()只有一个阻塞队列,notifyAll会唤起所有阻塞队列下的线程,而使用lock/condition,可以实现多个阻塞队列,signalAll只会唤起某个阻塞队列下的阻塞线程。

常用API方法

方法名称 描述
void await() throws InterruptedException 当前线程进入等待状态直到被通知(signal)或中断,当前线程进入运行状态且从await()方法返回的情况,包括1.其他线程调用该Condition的signal()活signalAll()方法,而当前线程被选中唤醒 2.其他线程(调用interrupt()方法)中断当前线程
void awaitUninterruptibly() 当前线程进入等待状态直到被通知,等待过程中不响应中断
long awaitNanos(long nanosTimeout) throws InterruptedException 当前线程进入等待状态直到被通知,中断,或者超时。返回值表示剩余的时间,如果在nanosTimeout纳秒之前被唤醒,那么返回值就是(nanosTimeout-实际耗时)。如果返回值是0或者负数,那么可以认定已经超时了
boolean await(long time, TimeUnit unit) throws InterruptedException 超时等待一段时间,如果没有通知就超时返回
boolean awaitUntil(Date deadline) throws InterruptedException 当前线程进入等待状态直到被通知,中断或者到某个时间。如果没有到指定时间就被通知,方法返回true,否则,表示到了指定时间,方法返回false
void signal() 唤醒一个等待在Condition上的线程,该线程从等待方法返回前必须获得与Condition相关联的锁
void signalAll() 唤醒所有等待在Condition上的线程,能够从等待方法返回的线程必须获得与Condition相关联的锁

注意:
  获取一个Condition必须通过Lock的newCondition()方法。
  Condition定义了等待/通知两种类型的方法,当前线程调用这些方法时,需要提前获取到Condition对象关联的锁。Condition对象是由Lock对象(调用Lock对象的newCondition()方法)创建出来的,换句话说,Condition是依赖Lock对象的。

发布了29 篇原创文章 · 获赞 47 · 访问量 8145

猜你喜欢

转载自blog.csdn.net/weixin_43767015/article/details/104933955