sleep和wait的区别

[TOC]

区别

1.sleep() 方法正在执行的线程主动让出 cpu(然后 cpu 就可以去执行其他任务),在 sleep 指定时间后 cpu 再回到该线程继续往下执行(注意:sleep 方法只让出了 cpu,而并不会释放同步资源锁);wait() 是 Object 的方法,调用会放弃对象锁,进入等待队列,待调用 notify()/notifyAll() 唤醒指定的线程或者所有线程,才会进入锁池,不再次获得对象锁才会进入运行状态。(注意:notify 的作用相当于叫醒睡着的人,而并不会给他分配任务,就是说 notify 只是让之前调用 wait 的线程有权利重新参与线程的调度);

2.sleep() 方法可以在任何地方使用,而 wait() 方法则只能在同步方法或同步块中使用;

wait和notify

wait、notify原理

在前面以经说到对象锁的本质,重量级锁模式时对象头是一个指向互斥量的指针,实际上互斥量就是一个监视器锁(ObjectMonitor)的数据结构,此时对象的hashCode、分代年龄等信息都会保存到对应的ObjectMonitor中,ObjectMonitor还有一些属性如recursion记录本锁被重入的次数,EntrySet记录想获取本锁的线程集合,WaitSet记录等待本锁的线程,TheOwner记录拥有本锁的线程对象。如下:

img

(图片来源于网络)

几个线程一起竞争对象的锁(EntrySet),只有一个能成功(acquire),成功的线程记录在The Owner中。调用wait、notify运行流程如下:

​ (1) 现有一个对象o,锁正在被线程 t1 持有,调用wait()方法后,线程 t1 将会被”晾到” (实际上仅仅是记录到) Wait Set 结构中。

​ (2)然后将会有另一个线程 t2 获取到锁,The Owner记录的变成了 t2 线程。

​ (3)t2 线程不需要 o的锁时,调用o.notify()/o.notifyAll()方法,对象o就会告诉 Wait Set结构中记录的线程们:你们又可以来竞争我啦,我的锁现在没被人持有。

简单的说就是:wait是对象通知持有自己锁的线程释放我的锁,notify()/notifyAll()就是对象通知刚刚被自己晾在一边的线程又可以来竞争我的锁了。我想到了一个比较贴切的比喻:

​ 客人(线程)来拜访主人(对象),必须获得主人的时间权(锁),且主人同时只能接待一人(互斥)。

​ 正在客厅接待一名客人时,因为一些原因主人必须先接待另一位客人,这时主人请当前客人去另一间房里等待,让出自己的时间权(wait方法)

​ 主人在客厅接待另一位客人,接待完毕后,让前一位(也可能有几位)在另一间房等待的客人再来到客厅,继续接待(notify/notifyAll方法)

调用 wait() 使得线程等待某个条件满足,线程在等待时会被挂起并释放锁。当其他线程的运行使得这个条件满足时,其它线程
会调用 notify() 。
它们都属于 Object 的一部分,而不属于 Thread。只能用在同步方法或者同步控制块中使用,

notify不会释放锁

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
public class Thread {

 public static void main(String[] args) {
 final Object object = new Object();
        Thread t1 = new Thread() {
            public void run()
            {
                synchronized (object) {
                    System.out.println("T1 start!");
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("T1 end!");
                }
            }
        };
        Thread t2 = new Thread() {
            public void run()
            {
                synchronized (object) {
                    System.out.println("T2 start!");
                    object.notify();
                    System.out.println("T2 end!");
                }
            }
        };
        
        t1.start();
        t2.start();
 
 }

}
1
2
3
4
T1 start!
T2 start!
T2 end!
T1 end!

和condition的对比

  1. \1. lock不再用synchronize把同步代码包装起来;
  2. \2. 阻塞需要另外一个对象condition;
  3. \3. 同步和唤醒的对象是condition而不是lock,对应的方法是await和signal,而不是wait和notify。

notify和notifyAll区别,什么时候用,什么时候唤醒一个线程,什么时候唤醒多个线程,举个例子?