介绍
Thread类中主要方法:
类 | 方法名 | 简介 |
---|---|---|
Thread | sleep相关 | 线程休眠指定时间 |
join | 执行此方法的线程等待此方法所属线程执行完毕 | |
yield | 放弃已经获取到的CPU资源,并马上进行资源竞争 | |
currentThread | 获取当前执行线程的引用 | |
start,run相关 | 启动线程相关 | |
Interrupt相关 | 中断线程 | |
stop(),suspend(),resume() | 已废弃 | |
Object | wait/notify/notifyAll相关 | 让线程暂时休息和唤醒 |
相关,指的重载方法,也就是方法名相同,但是参数不同,例如sleep有多个方法,只是参数不同,实际作用大同小异。
关于Thread类的众多方法在前面文章基本已经提到
本文主要介绍Object类的并发相关方法,最后附加介绍一些Thread类的方法
wait,notify,notifyAll方法详解
作用:阻塞阶段、唤醒阶段、遇到中断
阻塞阶段
执行wait方法时,必须拥有对象的monitor锁,当线程持有的对象monitor锁执行了wait方法后,就会进入阻塞阶段(不是先阻塞状态),并且会释放monitor锁,直到以下四种情况发生,才会被唤醒:
- 另一个线程调用这个对象的notify()方法且刚好被唤醒的是本线程
- 另一个线程调用这个对象的notifyAll()方法
- 超过了wait(long timeout)规定的超时时间,如果传入0,就是永久等待;
- 处于wait状态的线程自身收到了interrupt()信号
唤醒阶段
使用Object.xx方法,Object对象为线程进入wait方法前调用的monitor对象
- notify: 随机从处于wait状态的线程中,选择一个唤醒
- notifyAll:唤醒所有处于wait状态的线程
- 注意:唤醒后的线程不一定能获得monitor锁,没有抢到monitor锁的线程只能被阻塞,等待其他线程释放锁
遇到中断
- 线程持有的monitor锁对象执行了wait方法后,在等待时,收到了interrupt信号,就是遇到了中断
过程
- 设置一个对象,作为锁对象。
- 创建线程,在线程的run方法中创建 synchronized(monitor对象)代码块
- 当线程进入到synchronized的代码块后,可以使用 monitor对象.wait()方法,释放锁对象
- 当其他线程获取到锁对象后,使用锁对象.notify或锁对象的notifyAll方法,就可能唤醒之前进入wait状态的线程
- 被唤醒的线程,需要其他线程释放锁对象,才有可能重新获得锁对象,执行wait方法后面的内容
Object类常见问题
1、为什么wait()需要在同步代码块中使用,而sleep不需要
- 因为如果不在同步代码块中,那么可能在执行wait之前,突然线程切换到了另外的一个线程,也许另一个线程先把notify执行了,这样就无法唤醒这个线程了。
- sleep本身只是针对自己线程,与其他线程关心不大
2、 为什么线程通信的方法wait(),nofify()和notifyAll()被定义在Object类里?而sleep定义在 Thread类里?
- 因为wait,notify,notifyAll是属于锁级别的操作,而锁是属于某一个对象的,每一个对象的对象头中都含有几位保存锁的状态,所以锁实际上是绑定到某个对象中,而不是线程中。
- 如果定义到线程中,那么线程就只能有一个锁,实际上线程可能需要持有多把锁,多把锁相互配合
3、Wait方法是属于 Object对象的,那调用 Thread.wait会怎么样
1 | /** |
执行上面的代码,我们会发现程序会一直处于等待状态,永远不会结束;
当我们注释了 Thread.sleep(10)之后
1 | public class Test { |
此时我们就能发现,程序正常运行并最后退出了。
1 | As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances. |
这一段话是Java的java.lang.Thread#join(long)方法的java doc,就是说,在线程结束的时候,JVM会自动调用线程对象的notifyAll方法(为了配合join方法)。
由此我们能分析出来上面程序的过程:
- 在没有注释的时候,当t1线程启动后,main线程休眠了10ms
- 此时t1线程即使休眠了2ms也已经执行结束了,t1线程的notifyAll方法也已经调用完毕
- 然后main线程结束了休眠,进入了synchronized代码块,t1对象执行了wait方法
- 此时已经没有其他线程去执行 t1.notify或t1.notifyAll方法了,所以程序卡住了
- 当有注释的时候,main线程没有休眠,t1线程休眠了2ms,
- 此时main线程已经进入了synchronized代码块,执行了t1.wait()方法,然后t1线程休眠结束,执行t1.notifyAll方法,
- 唤醒了main线程,main线程离开synchronized代码块,
- 程序执行完毕
Thread方法
Sleep方法
作用:只想让线程在预期的时间执行,其他时候不要占用CPU资源
- 不释放锁:包括synchronized和lock
- 响应中断:
- 抛出InterrupedException
- 清除中断状态
sleep方法可以让线程进入Waiting状态,并且不占用CPU资源,但是不释放锁,直到规定时间后再执行,休眠期间如果被中断,会抛出异常并清除中断状态
wait/npotify、 sleep()异同
相同:
- 阻塞:让线程阻塞
- 响应中断:都可以对中断信号进行响应
不同:
- 同步方法中:wait和notify必须在同步方法中去执行,sleep不需要
- 释放锁: wait会让线程释放锁,sleep时线程不会释放锁
- 指定时间:sleep指定时间
- 所属类:wait/notify属于Object,sleep属于线程
join方法
作用:因为新的线程加入了我们,所以我们要等它执行完再出发
用法:main(主线程)等待thread1执行完毕,注意谁等谁。
- 在join期间,主线程是waiting状态
在main方法中的new一个Thread1,Thread1.join,是Thread1线程加入main方法的主线程,是主线程在等待Thread1
yield方法
作用:释放线程的CPU时间片
- 让出CPU后,会再次竞争CPU资源,如果竞争到,就会马上又开始执行
- sleep:线程调度器认为线程已经被阻塞了,不会被调度起来
参考
- 本文作者: xczll
- 本文链接: https://xczllgit.github.io/2020/03/14/concurrent/2020-03-14-threadMethods/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!