一 线程状态以及互相之间的转换
线程状态可以分为5种:
1. 初始状态:新建了一个线程对象
2. 就绪状态:(可运行状态)调用thread.start()之后
3. 运行状态:只有就绪状态下的线程获得了CPU时间块才会变成运行状态
4. 阻塞状态:
等待阻塞:调用了obj.wait()之后,调度程序会把线程放入等待池中,wait()会释放当前持有的锁。
同步阻塞:针对synchronized关键字,即别的线程持有锁,当前线程因为得不到锁而阻塞。
其他阻塞:thread.sleep(long_ms),thread.join(long_ms)或等待I/O请求时,JVM会把该线程设置为阻塞状态,sleep()不会释放线程持有的锁。
5. 终止状态:线程执行完毕会因为异常退出了run()方法。
二 常见的操作线程的方法
1. sleep(long): 使线程暂停执行(休眠)
(1)可能会抛出InterruptedException,因为异常无法跨线程传递,因此必须在当前线程处理;
(2)sleep()方法可以在任何地方调用,不一定非要在持有锁的状态下;
(3)是Thread类的静态方法,只能用来控制当前线程。
2. wait(long):使线程进入等待状态
(1)必须先获取到线程的锁,因此只能在同步方法或同步块调用,如果没有锁会抛出IllegalMonitorStateException;
(2)会释放当前持有的锁;
(3)线程会进入等待队列,直到被notify()或notifyAll()唤醒。
3. yield():给调度器一个信息:现在是切换时间片给其他线程的一个好时机。因此此方法只是建议,并不保证一定会切换。
4. join():在线程T1调用T2.join(),那么T1,T2就会从交替执行变为顺序执行,即,T1会等待T2执行完毕才会继续执行,通常用在T1需要T2的执行结果等的场景中
5. notify():用来唤醒wait()状态下的线程,并把锁赋予唤醒后的线程,因此和wait()一样,也必须先持有该对象的锁。但要注意,notify()之后等待线程不会立刻得到锁,而是要等到出了同步块之后才能获得锁,也就是说即使调用了notify(),等待对象也依然处于锁池状态,还有出了同步块才会变成就绪状态。notify()在挑选唤醒对象时是随机的。
6. notifyAll():和notify()类似,只不过会唤醒所有在该对象上wait()的线程,而且等第一个线程执行完释放锁之后其他wait()的线程会再次争抢该锁并执行,直到所有在该对象上wait()的线程都执行完毕。
7.wait()和sleep():
(1)如果线程T1想结束T2,则可以调用T2线程实例的interrupt()方法,并使T2抛出InterruptedException,但是如果T2当前不属于等待状态,而是在执行普通方法,那么会在它调用sleep,wait,join的时候立刻抛出InterruptedException。
(2)不同点:所属类(Object,Thread),是否释放锁,是否需要先获得锁。
8. sleep()和yield()区别:
(1)sleep()可以控制让出的时间,yield不能
(2)sleep有可能让出时间给低优先级的线程,yield只可能让给同优先级的线程。
[1] Java多线程创建和常用方法:https://www.jianshu.com/p/ebe99ede412a
[2] Java线程的6种状态及切换:https://blog.csdn.net/pange1991/article/details/53860651