Java编码中,经常遇到因为业务逻辑实现的需要会对线程进行人为的状态转换操作,最常用的就是阻塞,之前虽然用过阻塞,但是一直没有进行系统的总结。
总的来说,对线程状态的转换操作分成两大类:
Thread类的sleep,yield,join 方法
Object类的wait,notify,notifyall 方法
之前不熟悉时总是把sleep和wait混淆起来,其实两者的使用场景和机制差别很大。下面分别一一说明.
Sleep(longtime)
Sleep是带时间参数的,代表休眠多久,一般用法为Thread.sleep(3000),或者Thread.currentThread().sleep(1000);使用的是Thread的静态成员方法sleep,基本上上面两种用法可以使用在代码的任何地方,包括main方法和其他成员方法中,这是为什么呢,我网上查了很久没有找到底层的实现逻辑,只能简单认为在java中,任何代码都是在线程中执行的,包括main方法是在主线程中执行的,所以在代码的任何地方都能通过线程类Thread的静态方法进行阻塞操作。
Yield()
Yield方法没有参数,它的作用就是把现在正在执行的线程的状态由执行转成就绪状态,等待资源重新执行。除非cpu资源很紧张,否则一般线程调用Yield()后回立刻获得资源执行,所以基本上看不出有什么效果,而这个方法也比较少用。这里提下线程的三种状态的区别:阻塞,就绪,执行,执行很好理解,就是使用cpu执行代码指令,阻塞和就绪区别就比较明显,阻塞是被外界事件所阻塞的,比如我们在代码中调用了线程阻塞的方法Sleep或者wait,或者是被对象锁阻塞,简而言之就是不是因为cpu资源不足而不能执行,而是被其他事件所阻塞,等待事件完成或者通知后,转成就绪状态,获取到cpu资源就转成执行状态。
Join()
Join方法也是编码中常用的阻塞方法,主要是在主线程中等待其他子线程执行完run方法后再继续执行主线程的后续代码,当然也可以用到其他线程中。
上面所述都是Tread本身的阻塞机制,而下面所述的是关于和对象锁相关的,或者说和同步锁synchronized相关的
Wait()
Wait方法可以带时间参数也可以不带参数,一般用法是在synchronized语句块中,例如
synchronized(obj){
obj.wait(1000);
}
带参数时是阻塞1秒后无人唤醒则自动恢复就绪状态,无参数是指一直阻塞该线程知道有人唤醒。
Notify()
用法如下
synchronized(obj){
obj.Notify();
}
随机唤醒一个因为调用obj.wait()而阻塞等待的线程。
Notifyall()
用法如下
synchronized(obj){
obj.Notifyall();
}
唤醒所有因为调用obj.wait()而阻塞等待的线程。
Sleep和wait的区别:
Sleep阻塞当前线程,但是并不会释放cpu资源,并且一定休眠时间结束后才能执行后续语句,并不能被中途唤醒。而且不释放对象锁资源,正常,因为它跟对象锁毫无关系。
Wait只有在获取对象锁的情况下才能使用,说白了就是必须在synchronized(obj)语句块里执行,阻塞当前线程,并释放cpu资源,中途可以被其他线程调用Notify唤醒。并在阻塞的时间内,释放obj的对象锁,这样其他线程可以在此期间内获取对象锁从而执行synchronized里面的语句块,跟对象锁关系密切。
一般使用场景,如果是单线线程,比较简单地阻塞当前线程,效率要求不高的话,使用sleep即可,如果并发很大,效率要求比较高的话,使用obj.wait()来提高cpu的使用效率。