java线程中的wait和notify以及notifyall

本文详细解析了Java中线程同步机制的核心方法wait(), notify() 和 notifyAll() 的工作原理及使用方法,同时对比了这些方法与sleep()的区别,帮助读者深入理解线程间的同步与协作。

 

一、区别与联系

1.1、wait(),notify()和notifyAll()都是java.lang.Object的方法,而确实sleep方法是Thread类中的方法,这是为什么呢?

 

    因为wait和notify的本质是基于条件对象的,而且只能由已经获得锁的线程调用。java的每个Object都有一个隐式锁,这个隐式锁关联一个Condition条件对象,线程拿到这个隐式锁(比如进入synchronized代码区域),就可以调用wait,语义是在Condition条件对象上等待,其他的线程可以在这个Condition条件对象上等待,等满足条件之后,就可以调用notify或者notifyAll来唤醒所有在此条件对象上等待的线程。

 

1.2、sleep() 和 wait() 区别

 

  对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。

sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。

在调用sleep()方法的过程中,线程不会释放对象锁

而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。

 

1.3、notify和notifyAll区别

 

  当一个线程进入wait之后,就必须等其他线程notify/notifyall,使用notifyall,可以唤醒
所有处于wait状态的线程,使其重新进入锁的争夺队列中,而notify只能唤醒一个。注意,任何时候只有一个线程可以获得锁,也就是说只有一个线程可以运行synchronized 中的代码,notifyall只是让处于wait的线程重新拥有锁的争夺权,但是只会有一个获得锁并执行
notify表示唤醒一个线程,notifyAll也表示唤醒一个线程,但它会notify所有的线程,具体唤醒哪一个线程,由jvm来决定。 
 
1.4、wait和notify详解
  
  

wait()和notify()是直接隶属于Object类,也就是说,所有对象都拥有这一对方法。初看起来这十分 不可思议,但是实际上却是很自然的,因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的 wait() 方法导致线程阻塞,并且该对象上的锁被释放。而调用任意对象的notify()方法则导致因调用该对象的wait() 方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

其次,wait()和notify()可在任何位置调用,但是这一对方法却必须在 synchronized 方法或块中调用,理由也很简单,只有在 synchronized 方法或块中当前线程才占有锁,才有锁可以释放。同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以 释放。因此,方法调用必须放置在这样的 synchronized 方法或块中,该方法或块的加锁对象就是调用这些方法的对象。若不满足这一条 件,则程序虽然仍能编译,但在运行时会出现IllegalMonitorStateException 异常。

 wait() 和 notify() 方法的上述特性决定了它们经常和synchronized 方法或块一起使用,将它们和操作系统的进程间通信机制作 一个比较就会发现它们的相似性:synchronized方法或块提供了类似于操作系统原语的功能,它们的执行不会受到多线程机制的干扰,而这一对方法则 相当于 block 和wakeup 原语(这一对方法均声明为 synchronized)。它们的结合使得我们可以实现操作系统上一系列精妙的进程间 通信的算法(如信号量算法),并用于解决各种复杂的线程间通信问题。
 

关于 wait() 和 notify() 方法最后再说明两点:
    第一:调用 notify() 方法导致解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。
    第二:除了 notify(),还有一个方法 notifyAll() 也可起到类似作用,唯一的区别在于,调用 notifyAll() 方法将把因调 用该对象的 wait() 方法而阻塞的所有线程一次性全部解除阻塞。当然,只有获得锁的那一个线程才能进入可执行状态,

    具体唤醒哪一个线程,由jvm来决定。

相关wait和notify使用demo:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/**
  * <pre>
  * 子线程循环10次,接着主线程循环100次,接着有回到子线程循环10次,
  * 接着再回到主线程循环100次,如此执行50次
  * </pre>
  * @author ketqi
  */
  public class WaitNotifyDemo {
    public static void main(String[] args) {
  
      final Business business = new Business();
      new Thread( new Runnable() {
        @Override
        public void run() {
          for ( int i = 1 ; i <= 50 ; i++) {
            business.sub(i);
          }
  
        }
      }).start();
  
      for ( int i = 1 ; i <= 50 ; i++) {
        business.main(i);
      }
    }
  }
  
  class Business {
    private boolean isMainThread = true ;
  
    public synchronized void sub( int i) {
      while (!isMainThread) {
        try {
          this .wait();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
      for ( int j = 1 ; j <= 10 ; j++) {
        System.out.println( "sub thread sequence of " + j + ",loop of " + i);
     }
      isMainThread = false ;
      this .notify();
    }
  
   public synchronized void main( int i) {
      while (isMainThread) {
        try {
         this .wait();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
      for ( int j = 1 ; j <= 100 ; j++) {
        System.out.println( "main thread sequence of " + j + ",loop of " + i);
      }
      isMainThread = true ;
      this .notify();
    }
  }

转载于:https://www.cnblogs.com/geekdc/p/5262411.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值