Thread.sleep()和Object.wait()区别

区别

    下面将从调用方式、是否释放锁对象两方面进行阐述。

    Thread.sleep(long millis) 是Thread类的静态方法,作用是让当前正在执行的线程休眠(暂时停止执行)指定的毫秒数。

    原理是让出cpu时间片,把当前线程放入就绪线程队列中,直到睡眠时间到期自动苏醒,被调度为可执行线程(runnable)。sleep()方法作用的线程一定是当前正在运行的线程,如果代码在一个线程类中,不一定是代码所在的线程实例,即使是在线程对象上调用sleep()或者调用Thread.currentThread().sleep(),调用sleep()方法并不会释放对象锁

    object.wait()是实例对象(Object及其子类实列,也包括Class类实例)的方法,让当前正在执行的线程释放锁,直到被通知。

    object.wait(long timeout)也会让当前正在执行的线程释放锁,并在超时后会自动唤醒该线程重新竞争锁。在超时时间内也可调用notify/notifyAll进行唤醒。

    原理是让出cpu时间片,把当前正在执行的线程放入wait线程队列中,并把当前线程注册为指定对象的监听器,并释放指定对象的锁;当被notify/notifyAll通知时,重新进入Entry队列去,去竞争指定对象的锁,获得锁的线程进入就绪队列,等待被调度。

    wait()方法必须在sychronized代码块中,且锁定的对象要与等待通知来源的对象一致,即调用notify/notifyAll的实例对象是同一个。而wait(long)方法阻塞时放入的是就绪队列,等待时间到期或被通知就可被调度。

下面,我们来看下JDK源码对wait()的解释

1
2
3
4
5
* Causes the current thread to wait until another thread invokes the
* {@link java.lang.Object#notify()} method or the
* {@link java.lang.Object#notifyAll()} method for this object.
* In other words, this method behaves exactly as if it simply
* performs the call {@code wait(0)}.

    执行这个方法的线程会等待直到调用notify()/notifyAll()方法。调用wait() 是让调用方法的线程挂起,和调用的是哪个对象上的wait方法没关系。

题外话

锁对象

    每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列,就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程,当一个被线程被唤醒 (notify)后,才会进入到就绪队列,等待获得锁。

举例说明
    当一开始线程a第一次执行account.add方法时,jvm会检查锁对象account 的就绪队列是否已经有线程在等待,如果有则表明account的锁已经被占用了,由于是第一次运行,account的就绪队列为空,所以线程a获得了锁, 执行account.add方法。如果恰好在这个时候,线程b要执行account.withdraw方法,因为线程a已经获得了锁还没有释放,所以线程 b要进入account的就绪队列,等到得到锁后才可以执行。

一个线程执行临界区代码过程如下:

1、 获得同步锁
2、 清空工作内存
3、 从主存拷贝变量副本到工作内存
4、 对这些变量计算
5、 将变量从工作内存写回到主存
6、 释放锁

    可见,synchronized既保证了多线程的并发有序性,又保证了多线程的内存可见性。
线程释放锁的方式,通常是主动调用notify方法、同步代码块结束释放锁资源。同步代码块结束释放锁资源,对象就绪队列中的某一线程获得锁资源而开始线程;如果不使用notify,那么阻塞队列里线程将一直处于阻塞状态,即使就绪队列里线程都执行完了,阻塞队列里线程也将一直处于阻塞状态。

    notify不会释放锁,而是通知锁对象的阻塞队列里的某一线程(被阻塞,即主动调用wait方法),进入就绪队列。
    notifyAll 是 唤醒阻塞队列里的所有阻塞线程,他们都将进入就绪队列,而notify的数量是一个

Thread.yield与Thread.sleep区别

yield 即 “谦让”,也是 Thread 类的方法。它让掉当前线程 CPU 的时间片,使正在运行中的线程重新变成就绪状态,并重新竞争 CPU 的调度权。它可能会获取到,也有可能被其他线程获取到。

1)yield, sleep 都能暂停当前线程,sleep 可以指定具体休眠的时间,而 yield 则依赖 CPU 的时间片划分。

2)yield, sleep 两个在暂停过程中,如已经持有锁,则都不会释放锁资源。

3)yield 不能被中断,而 sleep 则可以接受中断。

参考资料