Java并发之(2):线程通信wait/notify(TIJ_21_5)

简介:   

java中线程间同步的最基本的方式就是使用wait()&notify()&notifyAll(),它们是线程间的握手机制。除了上述方法,java5还在java.util.concurrent.Locks包中提供了condition接口,该接口声明了awake()&signal()方法,也是线程间同步的方式之一。 wait()/notify() 都是任意java object所具备的方法,它们通常与synchronized关键字配合使用,而condition一般与显式的重入锁配合使用。

语义:

   Object.wait()把当前线程挂起,等待object.notify()或者object.notifyall()的执行。wait()可以替代busy waiting(通常以循环查询某条件是否已经具备的方式)。

object.notify()把等待object锁(monitor)的线程唤醒,通知其可以继续运行。如果有多个线程都在等待这个object的锁,则随机选择一个唤醒同时当前线程继续执行当另外一个线程调用该java对象的notify()方法时,将“唤醒”等待该java对象的那个线程。

notifyAll()将唤醒所有在等待的线程;notify()仅唤醒一个正在等待的线程,如果有多个线程在等待,会随机选择一个唤醒。

Wait:

为了正确地执行object.wait()方法,当前线程必须已经获得了该object的锁。也就是说,只有在同步方法或者同步块(synchronized)中,才能调用wait方法。执行object.wait()方法意味着当前线程释放该锁,当前线程挂起,直到有另外线程执行object.notify()方法。  wait()有三种方式,带纳秒参数的::

1  public final void wait(long timeout, int nanos) throws InterruptedException {

带毫秒参数的 :

1  public final native void wait(long timeout) throws InterruptedException;

和不带参数的:

1  public final void wait() throws InterruptedException {

其中wait()使用的比较多。

纳秒级的等待?

看wait的第一种实现,很有意思:

 1     public final void wait(long timeout, int nanos) throws InterruptedException {
 2         if (timeout < 0) {
 3             throw new IllegalArgumentException("timeout value is negative");
 4         }
 5 
 6         if (nanos < 0 || nanos > 999999) {
 7             throw new IllegalArgumentException(
 8                                 "nanosecond timeout value out of range");
 9         }
10 
11         if (nanos > 0) {
12             timeout++;
13         }
14 
15         wait(timeout);
16     }

 nanos好像并没有发挥什么作用。 这是Java8在Windows平台下的JDK的源码。注意如果是实时操作系统的话,nanos就有可能发挥作用,但是在Windows下,nanos目前并没有发挥作用。这是因为虽然cpu的时钟周期是纳秒级的,但是Windows操作系统并不支持这么细粒度的时间间隔。

在调用wait时还有一点要特别注意:

 永远在一个循环中调用wait()方法。因为如果多个线程都在等待同一个锁,然后其中一个得到了这个锁,然后会重置等待的条件,然后其他线程需要在它们醒来以后检查循环条件,以此来决定它们是需要再次等待,还是开始执行。

notify:

为了调用某个java对象的notify()方法,你必须在同一个java对象上同步(synchronized),或者说在一个同步的上下文中:

1 synchronized (someObject) {
2     someObject.wait(); // 释放锁,不对。应该在一个循环中调用wait方法
3 }
4 
5 /* different thread / object */
6 synchronized (someObject) {
7     someObject.notify();
8 }

 

尽量少使用wait/notify:

从Java5开始,java sdk中的java.util.conconrrent包中主要提供了三种与线程同步(不是synchronization,不是lock之类的线程互 斥)相关的类: Executor相关框架、并发容器(BlockinQueue)、和一些同步工具(Synchronizers,倒计数锁存器等)。这些同步工具主要是通用的同步类,主要用于线程间的协作。同步工具主要包括Condition、CountdownLatch、CyclicBarrier、Semaphore、CyclicBarrier、phasersexchangers等。

一般而言,在java5或者更新的java版本中,wait()&notify()很少显式地使用。因为很多标准库中的类已经实现了wait()/notify()的功能。比如:

  • 在Java5中,可以使用新的BlockingQueue实现,来实现生产者和消费者模型。
  • Java5中的显式锁类,比标准的synchronized关键字提供了更多的功能,比如带超时的tryLock。在Java5之前这个功能需要使用 wait()/notify()才能实现;
  • 可以使用Semaphore控制资源池。

操作系统线程问题中的概念

关键段CS critical section、事件Event、互斥量Mutex  信号量等概念 如何与java多线程中的概念映射???

互斥量:

跟锁的概念十分类似。

信号量:

操作系统的信号量是个很重要的概念,在进程控制方面都有应用。Java 并发库 的Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,acquire()获取一个许可,如果没有就等待,而 release()释放一个许可。比如在Windows下可以设置共享文件的最大客户端访问个数。

Semaphore维护了当前访问的个数,提供同步机制,控制同时访问的个数。在数据结构中链表可以保存“无限”的节点,用Semaphore可以实现有限大小的链表。另外重入锁ReentrantLock也可以实现该功能,但实现上要复杂些,代码也要复杂些。

参考文献:

http://blog.csdn.net/zyplus/article/details/6672775

http://javamex.com/tutorials/synchronization_wait_notify.shtml

http://www.javamex.com/tutorials/synchronization_producer_consumer.shtml

http://www.javamex.com/tutorials/synchronization_producer_consumer_2.shtml

http://www.javamex.com/tutorials/synchronization_concurrency_semaphore.shtml

http://blog.csdn.net/zyplus/article/details/6672775

http://www.javamex.com/tutorials/synchronization_wait_notify.shtml

http://blog.csdn.net/morewindows/article/details/7481609

http://blog.csdn.net/morewindows/article/details/7442639

http://blog.csdn.net/morewindows/article/details/7445233

http://blog.csdn.net/morewindows/article/details/7442333

http://stackoverflow.com/questions/6553225/whats-the-purpose-of-sleeplong-millis-int-nanos

原文地址:https://www.cnblogs.com/hzg1981/p/5038101.html