一.前言
关于并发编程这块, 线程的一些基础知识我们得搞明白, 本篇文章来说一下这两个方法的区别,对Android中的HandlerThread机制原理可以有更深的理解, HandlerThread源码理解,请查看笔者的这篇博客:
HandlerThread源码理解_handlerthread 源码_broadview_java的博客-CSDN博客
二.区别
2.1 sleep方法
public static native void sleep(long millis)
方法是Thread的静态方法,很显然它是让当前线程按照指定的时间休眠,其休眠时间的精度取决于处理器的计时器和调度器。需要注意的是如果当前线程获得了锁,sleep方法并不会失去锁。
Thread类中的静态方法sleep(),当一个执行中的线程调用了Thread的sleep()方法后,调用线程会暂时让出时间的执行权,这期间不参与cpu的调度,但是该线程持有的锁是不让出的。时间到了会正常返回,线程处于就绪状态,然后参与cpu调度,获取到cpu资源之后就可以运行。
我们通过这个Demo来看下
public class SleepTest {
private static Object lock = new Object();
public static void main(String[] args) {
new Thread(()->{
synchronized (lock) {
System.out.println("线程A休眠10秒不放弃锁");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A休眠10秒醒来");
}
}).start();
new Thread(()->{
synchronized (lock){
System.out.println("线程B休眠5秒不放弃锁");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B休眠5秒醒来");
}
}).start();
}
}
打印结果如下:
线程A休眠10秒不放弃锁
A休眠10秒醒来
线程B休眠5秒不放弃锁
B休眠5秒醒来
或
线程B休眠5秒不放弃锁
B休眠5秒醒来
线程A休眠10秒不放弃锁
A休眠10秒醒来
解释如下:
无论执行多少次,都是先A输出再B输出 或者 先B输出再A输出,不会出现交叉输出的情况,
因为A获取到锁之后,即使是sleep也不会释放锁,因B获取不到锁,也就无法执行。
总结: sleep方法不会释放锁
2.2 wait方法
Object.wait方法会释放锁对象
一个调用了某个对象的 Object.wait 方法的线程会等待另一个线程调用此对象的 Object.notify()方法 或 Object.notifyAll()方法。
其实waiting状态并不是一个线程的操作,它体现的是多个线程间的通信,可以理解为多个线程之间的协作关系, 也是java并发编程要处理的核心问题.
当多个线程协作时,比如A,B线程,如果A线程在Runnable(可运行)状态中调用了wait()方法那么A线程就进入了Waiting(无限等待)状态,同时失去了同步锁。
假如这个时候B线程获取到了同步锁,在运行状态中调用了notify()方法,那么就会将无限等待的A线程唤醒。
注意是唤醒,如果获取到锁对象,那么A线程唤醒后就进入Runnable(可运行)状态;如果没有获取锁对象,那么就进入到Blocked(锁阻塞状态)。
我们来看这个Demo :
public class wakeLockTest {
private static Object lock = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable1(), "线程1");
thread1.start();
Thread thread2 = new Thread(new Runnable2(), "线程2");
thread2.start();
}
static class Runnable1 implements Runnable{
@Override
public void run() {
synchronized (lock) {
try {
lock.wait();
System.out.println("当前线程名: "+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class Runnable2 implements Runnable{
@Override
public void run() {
synchronized (lock) {
lock.notify();
System.out.println("当前线程名: "+Thread.currentThread().getName());
}
}
}
}
如果不加同步锁相关的代码, 从代码流程来看, 线程1 先执行, 线程2再执行,肯定会打印
当前线程名: 线程1
当前线程名: 线程2
加入锁相关的代码后,打印如下:
三. 总结
sleep()方法是Thread类的方法,线程通过调用该方法,进入休眠状态主动让出CPU,从而CPU可以执行其他的线程。经过sleep指定的时间后,CPU回到这个线程上继续往下执行。
如何当前线程进入了同步锁,sleep()方法并不会释放锁。即使当前线程使用sleep方法让出了cpu,但其他被同步锁挡住了的线程也无法得到执行。
使用场合:
线程的调度执行是按照其优先级的高低顺序进行的,当高级别的线程未死亡时,低级别的线程没有机会获得CPU资源。有时优先级高的线程需要优先级低的线程完成一些辅助工作或者优先级高的线程需要完成一些比较费时的工作,此时优先级高的线程应该让出CPU资源,使得优先级低的线程有机会执行。为了达到这个目的,优先级高的线程可以在自己的run()方法中调用sleep方法来使自己放弃CPU资源,休眠一段时间。
wait()方法
Obj.wait(),与Obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,与notify是针对已经获取了Obj锁进行操作,从语法角度来说就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内。
从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。
相应的notify()就是对对象锁的唤醒操作。但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束后,才会释放锁.
两者区别
1.sleep()方法,属于Thread类的。; wait()方法,则是属于Object类的;
2.sleep方法不会释放锁,而wait方法会释放锁
3.wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
4.sleep可以自然醒,wait必须等待被唤醒.